From ac4607a893dbceca5d2dbb105557fbfae8bc1e4d Mon Sep 17 00:00:00 2001 From: gineshidalgo99 Date: Fri, 28 Apr 2017 13:51:53 -0400 Subject: [PATCH] Added face-hands hpp & render (no extraction code nor models) --- doc/UML/1_0_0rc1/UML.mdj | 12 +- doc/UML/1_0_0rc1/UML.pdf | Bin 38863 -> 38926 bytes .../tutorial_pose/1_extract_from_image.cpp | 8 +- .../2_extract_pose_or_heatmat_from_image.cpp | 6 +- .../3_user_input_processing_and_output.cpp | 4 +- ...user_input_processing_output_and_datum.cpp | 4 +- .../tutorial_wrapper/1_user_asynchronous.cpp | 2 +- .../tutorial_wrapper/2_user_synchronous.cpp | 4 +- include/openpose/core/arrayScaler.hpp | 24 --- include/openpose/core/datum.hpp | 19 +- include/openpose/core/headers.hpp | 6 +- include/openpose/core/keyPointScaler.hpp | 24 +++ include/openpose/core/scaleKeyPoints.hpp | 15 ++ include/openpose/core/scalePose.hpp | 15 -- .../{wArrayScaler.hpp => wKeyPointScaler.hpp} | 30 +-- .../experimental/face/enumClasses.hpp | 14 ++ .../experimental/face/faceExtractor.hpp | 66 ++++++ .../experimental/face/faceParameters.hpp | 35 +++ .../experimental/face/faceRenderGpu.hpp | 12 ++ .../experimental/face/faceRenderer.hpp | 33 +++ .../openpose/experimental/face/headers.hpp | 13 ++ .../experimental/face/wFaceExtractor.hpp | 91 ++++++++ .../experimental/face/wFaceRenderer.hpp | 88 ++++++++ .../filestream/wPoseJsonCocoSaver.hpp | 2 +- .../experimental/hand/enumClasses.hpp | 14 ++ .../handExtractor.hpp} | 16 +- .../experimental/hand/handParameters.hpp | 32 +++ .../experimental/hand/handRenderGpu.hpp | 12 ++ .../handRenderer.hpp} | 16 +- .../openpose/experimental/hand/headers.hpp | 13 ++ .../wHandExtractor.hpp} | 32 +-- .../wHandRenderer.hpp} | 30 +-- .../experimental/hands/enumClasses.hpp | 14 -- .../experimental/hands/handsParameters.hpp | 32 --- .../experimental/hands/handsRenderGpu.hpp | 12 -- .../openpose/experimental/hands/headers.hpp | 13 -- include/openpose/experimental/headers.hpp | 5 +- .../openpose/filestream/poseJsonCocoSaver.hpp | 2 +- include/openpose/filestream/poseJsonSaver.hpp | 2 +- include/openpose/filestream/poseSaver.hpp | 2 +- .../openpose/filestream/wPoseJsonSaver.hpp | 6 +- include/openpose/filestream/wPoseSaver.hpp | 6 +- include/openpose/gui/guiInfoAdder.hpp | 2 +- include/openpose/gui/wGuiInfoAdder.hpp | 2 +- .../openpose/pose/bodyPartConnectorBase.hpp | 4 +- .../openpose/pose/bodyPartConnectorCaffe.hpp | 4 +- include/openpose/pose/poseExtractor.hpp | 4 +- include/openpose/pose/poseParameters.hpp | 4 + include/openpose/pose/poseRenderer.hpp | 2 +- include/openpose/pose/wPoseExtractor.hpp | 2 +- include/openpose/pose/wPoseRenderer.hpp | 2 +- include/openpose/utilities/cuda.hu | 2 +- include/openpose/utilities/render.hu | 120 +++++++++++ include/openpose/wrapper/headers.hpp | 3 +- include/openpose/wrapper/wrapper.hpp | 118 ++++++++-- .../openpose/wrapper/wrapperStructFace.hpp | 32 +++ ...rStructHands.hpp => wrapperStructHand.hpp} | 14 +- .../openpose/wrapper/wrapperStructPose.hpp | 6 +- src/openpose/core/datum.cpp | 25 ++- src/openpose/core/defineTemplates.cpp | 2 +- .../{arrayScaler.cpp => keyPointScaler.cpp} | 32 +-- src/openpose/core/resizeAndMergeBase.cu | 2 +- .../{scalePose.cpp => scaleKeyPoints.cpp} | 30 +-- .../experimental/face/defineTemplates.cpp | 7 + .../experimental/face/faceExtractor.cpp | 154 +++++++++++++ .../experimental/face/faceRenderGpu.cu | 65 ++++++ .../experimental/face/faceRenderer.cpp | 66 ++++++ .../experimental/hand/defineTemplates.cpp | 7 + .../experimental/hand/handExtractor.cpp | 157 ++++++++++++++ .../experimental/hand/handRenderGpu.cu | 88 ++++++++ .../experimental/hand/handRenderer.cpp | 66 ++++++ .../experimental/hands/defineTemplates.cpp | 7 - .../experimental/hands/handsExtractor.cpp | 117 ---------- .../experimental/hands/handsRenderGpu.cu | 18 -- .../experimental/hands/handsRenderer.cpp | 48 ----- src/openpose/filestream/poseJsonCocoSaver.cpp | 12 +- src/openpose/filestream/poseJsonSaver.cpp | 8 +- src/openpose/filestream/poseSaver.cpp | 16 +- src/openpose/gui/guiInfoAdder.cpp | 4 +- src/openpose/pose/bodyPartConnectorBase.cpp | 24 +-- src/openpose/pose/bodyPartConnectorBase.cu | 8 +- src/openpose/pose/bodyPartConnectorCaffe.cpp | 8 +- src/openpose/pose/poseExtractor.cpp | 4 +- src/openpose/pose/poseExtractorCaffe.cpp | 4 +- src/openpose/pose/poseParameters.cpp | 37 ++++ src/openpose/pose/poseRenderGpu.cu | 204 +++--------------- src/openpose/pose/poseRenderer.cpp | 10 +- src/openpose/wrapper/wrapperStructFace.cpp | 12 ++ ...rStructHands.cpp => wrapperStructHand.cpp} | 4 +- 89 files changed, 1642 insertions(+), 711 deletions(-) delete mode 100644 include/openpose/core/arrayScaler.hpp create mode 100644 include/openpose/core/keyPointScaler.hpp create mode 100644 include/openpose/core/scaleKeyPoints.hpp delete mode 100644 include/openpose/core/scalePose.hpp rename include/openpose/core/{wArrayScaler.hpp => wKeyPointScaler.hpp} (60%) create mode 100644 include/openpose/experimental/face/enumClasses.hpp create mode 100644 include/openpose/experimental/face/faceExtractor.hpp create mode 100644 include/openpose/experimental/face/faceParameters.hpp create mode 100644 include/openpose/experimental/face/faceRenderGpu.hpp create mode 100644 include/openpose/experimental/face/faceRenderer.hpp create mode 100644 include/openpose/experimental/face/headers.hpp create mode 100644 include/openpose/experimental/face/wFaceExtractor.hpp create mode 100644 include/openpose/experimental/face/wFaceRenderer.hpp create mode 100644 include/openpose/experimental/hand/enumClasses.hpp rename include/openpose/experimental/{hands/handsExtractor.hpp => hand/handExtractor.hpp} (79%) create mode 100644 include/openpose/experimental/hand/handParameters.hpp create mode 100644 include/openpose/experimental/hand/handRenderGpu.hpp rename include/openpose/experimental/{hands/handsRenderer.hpp => hand/handRenderer.hpp} (57%) create mode 100644 include/openpose/experimental/hand/headers.hpp rename include/openpose/experimental/{hands/wHandsExtractor.hpp => hand/wHandExtractor.hpp} (64%) rename include/openpose/experimental/{hands/wHandsRenderer.hpp => hand/wHandRenderer.hpp} (66%) delete mode 100644 include/openpose/experimental/hands/enumClasses.hpp delete mode 100644 include/openpose/experimental/hands/handsParameters.hpp delete mode 100644 include/openpose/experimental/hands/handsRenderGpu.hpp delete mode 100644 include/openpose/experimental/hands/headers.hpp create mode 100644 include/openpose/utilities/render.hu create mode 100644 include/openpose/wrapper/wrapperStructFace.hpp rename include/openpose/wrapper/{wrapperStructHands.hpp => wrapperStructHand.hpp} (55%) rename src/openpose/core/{arrayScaler.cpp => keyPointScaler.cpp} (55%) rename src/openpose/core/{scalePose.cpp => scaleKeyPoints.cpp} (63%) create mode 100644 src/openpose/experimental/face/defineTemplates.cpp create mode 100644 src/openpose/experimental/face/faceExtractor.cpp create mode 100644 src/openpose/experimental/face/faceRenderGpu.cu create mode 100644 src/openpose/experimental/face/faceRenderer.cpp create mode 100644 src/openpose/experimental/hand/defineTemplates.cpp create mode 100644 src/openpose/experimental/hand/handExtractor.cpp create mode 100644 src/openpose/experimental/hand/handRenderGpu.cu create mode 100644 src/openpose/experimental/hand/handRenderer.cpp delete mode 100644 src/openpose/experimental/hands/defineTemplates.cpp delete mode 100644 src/openpose/experimental/hands/handsExtractor.cpp delete mode 100644 src/openpose/experimental/hands/handsRenderGpu.cu delete mode 100644 src/openpose/experimental/hands/handsRenderer.cpp create mode 100644 src/openpose/pose/poseParameters.cpp create mode 100644 src/openpose/wrapper/wrapperStructFace.cpp rename src/openpose/wrapper/{wrapperStructHands.cpp => wrapperStructHand.cpp} (50%) diff --git a/doc/UML/1_0_0rc1/UML.mdj b/doc/UML/1_0_0rc1/UML.mdj index c592aed2..59660927 100755 --- a/doc/UML/1_0_0rc1/UML.mdj +++ b/doc/UML/1_0_0rc1/UML.mdj @@ -5917,7 +5917,7 @@ "_parent": { "$ref": "AAAAAAFbHDZmRV10zeE=" }, - "name": "scalePose", + "name": "scaleKeyPoints", "ownedElements": [ { "_type": "UMLDependency", @@ -6005,7 +6005,7 @@ "_parent": { "$ref": "AAAAAAFbHDZmRV10zeE=" }, - "name": "wArrayScaler.hpp", + "name": "wKeyPointScaler.hpp", "ownedElements": [ { "_type": "UMLDependency", @@ -6076,7 +6076,7 @@ "_parent": { "$ref": "AAAAAAFbHDZmRV10zeE=" }, - "name": "ArrayScaler", + "name": "KeyPointScaler", "ownedElements": [ { "_type": "UMLDependency", @@ -13344,7 +13344,7 @@ "height": 13, "autoResize": false, "underline": false, - "text": "scalePose", + "text": "scaleKeyPoints", "horizontalAlignment": 2, "verticalAlignment": 5, "wordWrap": false @@ -13635,7 +13635,7 @@ "height": 13, "autoResize": false, "underline": false, - "text": "wArrayScaler.hpp", + "text": "wKeyPointScaler.hpp", "horizontalAlignment": 2, "verticalAlignment": 5, "wordWrap": false @@ -14633,7 +14633,7 @@ "height": 13, "autoResize": false, "underline": false, - "text": "ArrayScaler", + "text": "KeyPointScaler", "horizontalAlignment": 2, "verticalAlignment": 5, "wordWrap": false diff --git a/doc/UML/1_0_0rc1/UML.pdf b/doc/UML/1_0_0rc1/UML.pdf index 60481c2103a7c6f802e66e54b078cd6ea3e14710..0c55150584705602d055920757ee2a27e968b0fb 100644 GIT binary patch delta 18347 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 qFfcG6ZXgOUFfcGMFf=qZHD diff --git a/examples/tutorial_pose/1_extract_from_image.cpp b/examples/tutorial_pose/1_extract_from_image.cpp index 73f98722..8533d4c8 100644 --- a/examples/tutorial_pose/1_extract_from_image.cpp +++ b/examples/tutorial_pose/1_extract_from_image.cpp @@ -117,11 +117,11 @@ int openPoseTutorialPose1() double scaleInputToOutput; op::Array outputArray; std::tie(scaleInputToOutput, outputArray) = cvMatToOpOutput.format(inputImage); - // Step 3 - Estimate pose + // Step 3 - Estimate poseKeyPoints poseExtractorCaffe.forwardPass(netInputArray, inputImage.size()); - const auto pose = poseExtractorCaffe.getPose(); - // Step 4 - Render pose - poseRenderer.renderPose(outputArray, pose); + const auto poseKeyPoints = poseExtractorCaffe.getPoseKeyPoints(); + // Step 4 - Render poseKeyPoints + poseRenderer.renderPose(outputArray, poseKeyPoints); // Step 5 - OpenPose output format to cv::Mat auto outputImage = opOutputToCvMat.formatToCvMat(outputArray); diff --git a/examples/tutorial_pose/2_extract_pose_or_heatmat_from_image.cpp b/examples/tutorial_pose/2_extract_pose_or_heatmat_from_image.cpp index fadc51e6..b4de4cfa 100644 --- a/examples/tutorial_pose/2_extract_pose_or_heatmat_from_image.cpp +++ b/examples/tutorial_pose/2_extract_pose_or_heatmat_from_image.cpp @@ -121,12 +121,12 @@ int openPoseTutorialPose2() double scaleInputToOutput; op::Array outputArray; std::tie(scaleInputToOutput, outputArray) = cvMatToOpOutput.format(inputImage); - // Step 3 - Estimate pose + // Step 3 - Estimate poseKeyPoints poseExtractorPtr->forwardPass(netInputArray, inputImage.size()); - const auto pose = poseExtractorPtr->getPose(); + const auto poseKeyPoints = poseExtractorPtr->getPoseKeyPoints(); const auto scaleNetToOutput = poseExtractorPtr->getScaleNetToOutput(); // Step 4 - Render pose - poseRenderer.renderPose(outputArray, pose, scaleNetToOutput); + poseRenderer.renderPose(outputArray, poseKeyPoints, scaleNetToOutput); // Step 5 - OpenPose output format to cv::Mat auto outputImage = opOutputToCvMat.formatToCvMat(outputArray); diff --git a/examples/tutorial_thread/3_user_input_processing_and_output.cpp b/examples/tutorial_thread/3_user_input_processing_and_output.cpp index 3da831a0..ebb21608 100644 --- a/examples/tutorial_thread/3_user_input_processing_and_output.cpp +++ b/examples/tutorial_thread/3_user_input_processing_and_output.cpp @@ -114,7 +114,7 @@ public: { // User's post-processing (after OpenPose processing & before OpenPose outputs) here // datum.cvOutputData: rendered frame with pose or heatmaps - // datum.pose: Array with the estimated pose + // datum.poseKeyPoints: Array with the estimated pose try { if (datumsPtr != nullptr && !datumsPtr->empty()) @@ -142,7 +142,7 @@ public: { // User's displaying/saving/other processing here // datum.cvOutputData: rendered frame with pose or heatmaps - // datum.pose: Array with the estimated pose + // datum.poseKeyPoints: Array with the estimated pose if (datumsPtr != nullptr && !datumsPtr->empty()) { cv::imshow("User worker GUI", datumsPtr->at(0).cvOutputData); diff --git a/examples/tutorial_thread/4_user_input_processing_output_and_datum.cpp b/examples/tutorial_thread/4_user_input_processing_output_and_datum.cpp index 737620c0..c5d2e081 100644 --- a/examples/tutorial_thread/4_user_input_processing_output_and_datum.cpp +++ b/examples/tutorial_thread/4_user_input_processing_output_and_datum.cpp @@ -126,7 +126,7 @@ public: { // User's post-processing (after OpenPose processing & before OpenPose outputs) here // datum.cvOutputData: rendered frame with pose or heatmaps - // datum.pose: Array with the estimated pose + // datum.poseKeyPoints: Array with the estimated pose try { if (datumsPtr != nullptr && !datumsPtr->empty()) @@ -154,7 +154,7 @@ public: { // User's displaying/saving/other processing here // datum.cvOutputData: rendered frame with pose or heatmaps - // datum.pose: Array with the estimated pose + // datum.poseKeyPoints: Array with the estimated pose if (datumsPtr != nullptr && !datumsPtr->empty()) { cv::imshow("User worker GUI", datumsPtr->at(0).cvOutputData); diff --git a/examples/tutorial_wrapper/1_user_asynchronous.cpp b/examples/tutorial_wrapper/1_user_asynchronous.cpp index 8841e843..509afa8d 100644 --- a/examples/tutorial_wrapper/1_user_asynchronous.cpp +++ b/examples/tutorial_wrapper/1_user_asynchronous.cpp @@ -176,7 +176,7 @@ public: { // User's displaying/saving/other processing here // datum.cvOutputData: rendered frame with pose or heatmaps - // datum.pose: Array with the estimated pose + // datum.poseKeyPoints: Array with the estimated pose if (datumsPtr != nullptr && !datumsPtr->empty()) { cv::imshow("User worker GUI", datumsPtr->at(0).cvOutputData); diff --git a/examples/tutorial_wrapper/2_user_synchronous.cpp b/examples/tutorial_wrapper/2_user_synchronous.cpp index d37ff4f6..bafabf1b 100644 --- a/examples/tutorial_wrapper/2_user_synchronous.cpp +++ b/examples/tutorial_wrapper/2_user_synchronous.cpp @@ -188,7 +188,7 @@ public: { // User's post-processing (after OpenPose processing & before OpenPose outputs) here // datum.cvOutputData: rendered frame with pose or heatmaps - // datum.pose: Array with the estimated pose + // datum.poseKeyPoints: Array with the estimated pose try { if (datumsPtr != nullptr && !datumsPtr->empty()) @@ -216,7 +216,7 @@ public: { // User's displaying/saving/other processing here // datum.cvOutputData: rendered frame with pose or heatmaps - // datum.pose: Array with the estimated pose + // datum.poseKeyPoints: Array with the estimated pose if (datumsPtr != nullptr && !datumsPtr->empty()) { cv::imshow("User worker GUI", datumsPtr->at(0).cvOutputData); diff --git a/include/openpose/core/arrayScaler.hpp b/include/openpose/core/arrayScaler.hpp deleted file mode 100644 index 1bfe9803..00000000 --- a/include/openpose/core/arrayScaler.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef OPENPOSE__CORE__ARRAYS_SCALER_HPP -#define OPENPOSE__CORE__ARRAYS_SCALER_HPP - -#include -#include "array.hpp" -#include "enumClasses.hpp" - -namespace op -{ - class ArrayScaler - { - public: - explicit ArrayScaler(const ScaleMode scalePose); - - void scale(Array& array, const double scaleInputToOutput, const double scaleNetToOutput, const cv::Size& producerSize) const; - - void scale(std::vector>& arrays, const double scaleInputToOutput, const double scaleNetToOutput, const cv::Size& producerSize) const; - - private: - const ScaleMode mScaleMode; - }; -} - -#endif // OPENPOSE__CORE__ARRAYS_SCALER_HPP diff --git a/include/openpose/core/datum.hpp b/include/openpose/core/datum.hpp index 7d247058..22e558f1 100644 --- a/include/openpose/core/datum.hpp +++ b/include/openpose/core/datum.hpp @@ -52,15 +52,15 @@ namespace op // -------------------------------------------------- Resulting Array data parameters -------------------------------------------------- // /** - * Pose (x,y,score) locations for each person in the image. + * Body pose (x,y,score) locations for each person in the image. * It has been resized to the desired output resolution (e.g. `resolution` flag in the demo). * If outputData is empty, then cvOutputData will also be empty. * Size: #people x #body parts (e.g. 18 for COCO or 15 for MPI) x 3 ((x,y) coordinates + score) */ + Array poseKeyPoints; - Array pose; /** - * Pose heatmaps (body parts, background and PAFs) for the whole image. + * Body pose heatmaps (body parts, background and/or PAFs) for the whole image. * This parameters is by default empty and disabled for performance. Each group (body parts, background and PAFs) can be individually enabled. * #heatmaps = #body parts (if enabled) + 1 (if background enabled) + 2 x #PAFs (if enabled). Each PAF has 2 consecutive channels, one for x- and one for y-coordinates. * Order heatmaps: body parts + background (as appears in POSE_BODY_PART_MAPPING) + (x,y) channel of each PAF (sorted as appears in POSE_BODY_PART_PAIRS). See `pose/poseParameters.hpp`. @@ -71,10 +71,17 @@ namespace op /** * Experimental (NOT IMPLEMENTED YET) - * Hands code is in development phase. Not included in this version. - * Size: 2 (right and left hand) x #hand parts (21) x 3 ((x,y) coordinates + score) + * Face code is in development phase. Not included in this version. + * Size: #people's faces to render x #face parts (71) x 3 ((x,y) coordinates + score) */ - Array hands; + Array faceKeyPoints; + + /** + * Experimental (NOT IMPLEMENTED YET) + * Hands code is in development phase. Not included in this version. + * Size: #people's hands to render x 2 (right and left hand) x #hand parts (21) x 3 ((x,y) coordinates + score) + */ + Array handKeyPoints; // -------------------------------------------------- Other parameters -------------------------------------------------- // double scaleInputToOutput; /**< Scale ratio between the input Datum::cvInputData and the output Datum::cvOutputData. */ diff --git a/include/openpose/core/headers.hpp b/include/openpose/core/headers.hpp index fe57221d..1075cf7c 100644 --- a/include/openpose/core/headers.hpp +++ b/include/openpose/core/headers.hpp @@ -3,11 +3,11 @@ // core module #include "array.hpp" -#include "arrayScaler.hpp" #include "cvMatToOpInput.hpp" #include "cvMatToOpOutput.hpp" #include "datum.hpp" #include "enumClasses.hpp" +#include "keyPointScaler.hpp" #include "net.hpp" #include "netCaffe.hpp" #include "nmsBase.hpp" @@ -16,10 +16,10 @@ #include "renderer.hpp" #include "resizeAndMergeBase.hpp" #include "resizeAndMergeCaffe.hpp" -#include "scalePose.hpp" -#include "wArrayScaler.hpp" +#include "scaleKeyPoints.hpp" #include "wCvMatToOpInput.hpp" #include "wCvMatToOpOutput.hpp" +#include "wKeyPointScaler.hpp" #include "wOpOutputToCvMat.hpp" #endif // OPENPOSE__CORE__HEADERS_HPP diff --git a/include/openpose/core/keyPointScaler.hpp b/include/openpose/core/keyPointScaler.hpp new file mode 100644 index 00000000..27752d23 --- /dev/null +++ b/include/openpose/core/keyPointScaler.hpp @@ -0,0 +1,24 @@ +#ifndef OPENPOSE__CORE__KEY_POINT_SCALER_HPP +#define OPENPOSE__CORE__KEY_POINT_SCALER_HPP + +#include +#include "array.hpp" +#include "enumClasses.hpp" + +namespace op +{ + class KeyPointScaler + { + public: + explicit KeyPointScaler(const ScaleMode scaleMode); + + void scale(Array& arrayToScale, const double scaleInputToOutput, const double scaleNetToOutput, const cv::Size& producerSize) const; + + void scale(std::vector>& arraysToScale, const double scaleInputToOutput, const double scaleNetToOutput, const cv::Size& producerSize) const; + + private: + const ScaleMode mScaleMode; + }; +} + +#endif // OPENPOSE__CORE__KEY_POINT_SCALER_HPP diff --git a/include/openpose/core/scaleKeyPoints.hpp b/include/openpose/core/scaleKeyPoints.hpp new file mode 100644 index 00000000..9e193b1e --- /dev/null +++ b/include/openpose/core/scaleKeyPoints.hpp @@ -0,0 +1,15 @@ +#ifndef OPENPOSE__CORE__SCALE_KEY_POINTS_HPP +#define OPENPOSE__CORE__SCALE_KEY_POINTS_HPP + +#include "array.hpp" + +namespace op +{ + void scaleKeyPoints(Array& keyPoints, const double scale); + + void scaleKeyPoints(Array& keyPoints, const double scaleX, const double scaleY); + + void scaleKeyPoints(Array& keyPoints, const double scaleX, const double scaleY, const double offsetX, const double offsetY); +} + +#endif // OPENPOSE__CORE__SCALE_KEY_POINTS_HPP diff --git a/include/openpose/core/scalePose.hpp b/include/openpose/core/scalePose.hpp deleted file mode 100644 index 9ad9fbed..00000000 --- a/include/openpose/core/scalePose.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef OPENPOSE__CORE__SCALE_POSE_HPP -#define OPENPOSE__CORE__SCALE_POSE_HPP - -#include "array.hpp" - -namespace op -{ - void scalePose(Array& pose, const double scale); - - void scalePose(Array& pose, const double scaleX, const double scaleY); - - void scalePose(Array& pose, const double scaleX, const double scaleY, const double offsetX, const double offsetY); -} - -#endif // OPENPOSE__CORE__SCALE_POSE_HPP diff --git a/include/openpose/core/wArrayScaler.hpp b/include/openpose/core/wKeyPointScaler.hpp similarity index 60% rename from include/openpose/core/wArrayScaler.hpp rename to include/openpose/core/wKeyPointScaler.hpp index 5811e112..3d8e65a1 100644 --- a/include/openpose/core/wArrayScaler.hpp +++ b/include/openpose/core/wKeyPointScaler.hpp @@ -1,24 +1,24 @@ -#ifndef OPENPOSE__CORE__W_ARRAYS_SCALER_HPP -#define OPENPOSE__CORE__W_ARRAYS_SCALER_HPP +#ifndef OPENPOSE__CORE__W_KEY_POINT_SCALER_HPP +#define OPENPOSE__CORE__W_KEY_POINT_SCALER_HPP #include "../thread/worker.hpp" -#include "arrayScaler.hpp" -#include "scalePose.hpp" +#include "keyPointScaler.hpp" +#include "scaleKeyPoints.hpp" namespace op { template - class WArrayScaler : public Worker + class WKeyPointScaler : public Worker { public: - explicit WArrayScaler(const std::shared_ptr& arrayScaler); + explicit WKeyPointScaler(const std::shared_ptr& keyPointScaler); void initializationOnThread(); void work(TDatums& tDatums); private: - std::shared_ptr spArrayScaler; + std::shared_ptr spKeyPointScaler; }; } @@ -34,18 +34,18 @@ namespace op namespace op { template - WArrayScaler::WArrayScaler(const std::shared_ptr& arrayScaler) : - spArrayScaler{arrayScaler} + WKeyPointScaler::WKeyPointScaler(const std::shared_ptr& keyPointScaler) : + spKeyPointScaler{keyPointScaler} { } template - void WArrayScaler::initializationOnThread() + void WKeyPointScaler::initializationOnThread() { } template - void WArrayScaler::work(TDatums& tDatums) + void WKeyPointScaler::work(TDatums& tDatums) { try { @@ -58,8 +58,8 @@ namespace op // Rescale pose data for (auto& tDatum : *tDatums) { - std::vector> arrays{tDatum.pose, tDatum.hands}; - spArrayScaler->scale(arrays, tDatum.scaleInputToOutput, tDatum.scaleNetToOutput, tDatum.cvInputData.size()); + std::vector> arraysToScale{tDatum.poseKeyPoints, tDatum.handKeyPoints, tDatum.faceKeyPoints}; + spKeyPointScaler->scale(arraysToScale, tDatum.scaleInputToOutput, tDatum.scaleNetToOutput, tDatum.cvInputData.size()); } // Profiling speed Profiler::timerEnd(profilerKey); @@ -76,7 +76,7 @@ namespace op } } - COMPILE_TEMPLATE_DATUM(WArrayScaler); + COMPILE_TEMPLATE_DATUM(WKeyPointScaler); } -#endif // OPENPOSE__CORE__W_ARRAYS_SCALER_HPP +#endif // OPENPOSE__CORE__W_KEY_POINT_SCALER_HPP diff --git a/include/openpose/experimental/face/enumClasses.hpp b/include/openpose/experimental/face/enumClasses.hpp new file mode 100644 index 00000000..be06948e --- /dev/null +++ b/include/openpose/experimental/face/enumClasses.hpp @@ -0,0 +1,14 @@ +#ifndef OPENPOSE__FACE__ENUM_CLASSES_HPP +#define OPENPOSE__FACE__ENUM_CLASSES_HPP + +namespace op +{ + enum class FaceProperty : bool + { + NMSThreshold = 0, + Size, + }; +} + +#endif // OPENPOSE__FACE__ENUM_CLASSES_HPP + diff --git a/include/openpose/experimental/face/faceExtractor.hpp b/include/openpose/experimental/face/faceExtractor.hpp new file mode 100644 index 00000000..b298313a --- /dev/null +++ b/include/openpose/experimental/face/faceExtractor.hpp @@ -0,0 +1,66 @@ +#ifndef OPENPOSE__FACE__FACE_EXTRACTOR_HPP +#define OPENPOSE__FACE__FACE_EXTRACTOR_HPP + +#include +#include +#include // std::shared_ptr +#include +#include +#include "../../core/array.hpp" +#include "../../core/net.hpp" +#include "../../core/nmsCaffe.hpp" +#include "../../core/resizeAndMergeCaffe.hpp" +#include "../../pose/enumClasses.hpp" +#include "enumClasses.hpp" + +namespace op +{ + namespace experimental + { + class FaceExtractor + { + public: + explicit FaceExtractor(const std::string& modelFolder, const int gpuId, const PoseModel poseModel); + + void initializationOnThread(); + + void forwardPass(const Array& poseKeyPoints, const cv::Mat& cvInputData); + + Array getFaceKeyPoints() const; + + double get(const FaceProperty property) const; + + void set(const FaceProperty property, const double value); + + void increase(const FaceProperty property, const double value); + + private: + const cv::Size mNetOutputSize; + const cv::Size mOutputSize; + const unsigned char mNeck; + const unsigned char mNose; + const unsigned char mLEar; + const unsigned char mREar; + const unsigned char mLEye; + const unsigned char mREye; + std::array, (int)FaceProperty::Size> mProperties; + std::shared_ptr spNet; + std::shared_ptr> spResizeAndMergeCaffe; + std::shared_ptr> spNmsCaffe; + Array mFaceImageCrop; + Array mFaceKeyPoints; + float mScaleFace; + // Init with thread + boost::shared_ptr> spCaffeNetOutputBlob; + std::shared_ptr> spHeatMapsBlob; + std::shared_ptr> spPeaksBlob; + std::thread::id mThreadId; + + void checkThread() const; + + DELETE_COPY(FaceExtractor); + }; + } +} + +#endif // OPENPOSE__FACE__FACE_EXTRACTOR_HPP diff --git a/include/openpose/experimental/face/faceParameters.hpp b/include/openpose/experimental/face/faceParameters.hpp new file mode 100644 index 00000000..9c58b057 --- /dev/null +++ b/include/openpose/experimental/face/faceParameters.hpp @@ -0,0 +1,35 @@ +#ifndef OPENPOSE__FACE__FACE_PARAMETERS_HPP +#define OPENPOSE__FACE__FACE_PARAMETERS_HPP + +#include "enumClasses.hpp" +#include "../../pose/poseParameters.hpp" + +namespace op +{ + const unsigned char FACE_MAX_NUMBER_FACE = 1; + + const unsigned char FACE_NUMBER_PARTS = 71; + #define FACE_PAIRS_TO_RENDER {0,1, 1,2, 2,3, 3,4, 4,5, 5,6, 6,7, 7,8, 8,9, 9,10, 10,11, 11,12, 12,13, 13,14, 14,15, 15,16, 17,18, 18,19, 19,20, \ + 20,21, 22,23, 23,24, 24,25, 25,26, 27,28, 28,29, 29,30, 31,32, 32,33, 33,34, 34,35, 36,37, 37,38, 38,39, 39,40, 40,41, \ + 41,36, 42,43, 43,44, 44,45, 45,46, 46,47, 47,42, 48,49, 49,50, 50,51, 51,52, 52,53, 53,54, 54,55, 55,56, 56,57, 57,58, \ + 58,59, 59,48, 60,61, 61,62, 62,63, 63,64, 64,65, 65,66, 66,67, 67,60} + + // Constant Global Parameters + // const unsigned char FACE_MAX_PEOPLE = 1; + + // Constant parameters + const auto FACE_CCN_DECREASE_FACTOR = 8.f; + const unsigned int FACE_MAX_PEAKS = 64u; + const std::string FACE_PROTOTXT{"face/pose_deploy.prototxt"}; + const std::string FACE_TRAINED_MODEL{"face/pose_iter_116000.caffemodel"}; + + // Default Model Parameters + // They might be modified on running time + const auto FACE_DEFAULT_NMS_THRESHOLD = 0.1f; + + // Rendering default parameters + const auto FACE_DEFAULT_ALPHA_FACE = POSE_DEFAULT_ALPHA_POSE; + // const auto FACE_DEFAULT_ALPHA_HEATMAP = POSE_DEFAULT_ALPHA_HEATMAP; +} + +#endif // OPENPOSE__FACE__FACE_PARAMETERS_HPP diff --git a/include/openpose/experimental/face/faceRenderGpu.hpp b/include/openpose/experimental/face/faceRenderGpu.hpp new file mode 100644 index 00000000..80cdaab7 --- /dev/null +++ b/include/openpose/experimental/face/faceRenderGpu.hpp @@ -0,0 +1,12 @@ +#ifndef OPENPOSE__FACE__GPU_FACE_RENDER_HPP +#define OPENPOSE__FACE__GPU_FACE_RENDER_HPP + +#include +#include "faceParameters.hpp" + +namespace op +{ + void renderFaceGpu(float* framePtr, const cv::Size& frameSize, const float* const facePtr, const int numberFace, const float alphaColorToAdd = FACE_DEFAULT_ALPHA_FACE); +} + +#endif // OPENPOSE__FACE__GPU_FACE_RENDER_HPP diff --git a/include/openpose/experimental/face/faceRenderer.hpp b/include/openpose/experimental/face/faceRenderer.hpp new file mode 100644 index 00000000..d366b708 --- /dev/null +++ b/include/openpose/experimental/face/faceRenderer.hpp @@ -0,0 +1,33 @@ +#ifndef OPENPOSE__FACE__FACE_RENDERER_HPP +#define OPENPOSE__FACE__FACE_RENDERER_HPP + +#include +#include "../../core/array.hpp" +#include "../../core/renderer.hpp" +#include "../../thread/worker.hpp" + +namespace op +{ + namespace experimental + { + class FaceRenderer : public Renderer + { + public: + explicit FaceRenderer(const cv::Size& frameSize); + + ~FaceRenderer(); + + void initializationOnThread(); + + void renderFace(Array& outputData, const Array& faceKeyPoints); + + private: + const cv::Size mFrameSize; + float* pGpuFace; // GPU aux memory + + DELETE_COPY(FaceRenderer); + }; + } +} + +#endif // OPENPOSE__FACE__FACE_RENDERER_HPP diff --git a/include/openpose/experimental/face/headers.hpp b/include/openpose/experimental/face/headers.hpp new file mode 100644 index 00000000..4829481b --- /dev/null +++ b/include/openpose/experimental/face/headers.hpp @@ -0,0 +1,13 @@ +#ifndef OPENPOSE__FACE__HEADERS_HPP +#define OPENPOSE__FACE__HEADERS_HPP + +// face module +#include "enumClasses.hpp" +#include "faceExtractor.hpp" +#include "faceParameters.hpp" +#include "faceRenderer.hpp" +#include "faceRenderGpu.hpp" +#include "wFaceExtractor.hpp" +#include "wFaceRenderer.hpp" + +#endif // OPENPOSE__FACE__HEADERS_HPP diff --git a/include/openpose/experimental/face/wFaceExtractor.hpp b/include/openpose/experimental/face/wFaceExtractor.hpp new file mode 100644 index 00000000..dd6c8373 --- /dev/null +++ b/include/openpose/experimental/face/wFaceExtractor.hpp @@ -0,0 +1,91 @@ +#ifndef OPENPOSE__FACE__W_FACE_EXTRACTOR_HPP +#define OPENPOSE__FACE__W_FACE_EXTRACTOR_HPP + +#include // std::shared_ptr +#include "../../thread/worker.hpp" +#include "faceRenderer.hpp" + +namespace op +{ + namespace experimental + { + template + class WFaceExtractor : public Worker + { + public: + explicit WFaceExtractor(const std::shared_ptr& faceExtractor); + + void initializationOnThread(); + + void work(TDatums& tDatums); + + private: + std::shared_ptr spFaceExtractor; + + DELETE_COPY(WFaceExtractor); + }; + } +} + + + + + +// Implementation +#include "../../utilities/errorAndLog.hpp" +#include "../../utilities/macros.hpp" +#include "../../utilities/pointerContainer.hpp" +#include "../../utilities/profiler.hpp" +namespace op +{ + namespace experimental + { + template + WFaceExtractor::WFaceExtractor(const std::shared_ptr& faceExtractor) : + spFaceExtractor{faceExtractor} + { + } + + template + void WFaceExtractor::initializationOnThread() + { + spFaceExtractor->initializationOnThread(); + } + + template + void WFaceExtractor::work(TDatums& tDatums) + { + try + { + if (checkNoNullNorEmpty(tDatums)) + { + // Debugging log + dLog("", Priority::Low, __LINE__, __FUNCTION__, __FILE__); + // Profiling speed + const auto profilerKey = Profiler::timerInit(__LINE__, __FUNCTION__, __FILE__); + // Extract people face + for (auto& tDatum : *tDatums) + { + spFaceExtractor->forwardPass(tDatum.poseKeyPoints, tDatum.cvInputData); + tDatum.faceKeyPoints = spFaceExtractor->getFaceKeyPoints(); + } + // Profiling speed + Profiler::timerEnd(profilerKey); + Profiler::printAveragedTimeMsOnIterationX(profilerKey, __LINE__, __FUNCTION__, __FILE__, 1000); + // Debugging log + dLog("", Priority::Low, __LINE__, __FUNCTION__, __FILE__); + } + } + catch (const std::exception& e) + { + this->stop(); + tDatums = nullptr; + error(e.what(), __LINE__, __FUNCTION__, __FILE__); + } + } + + COMPILE_TEMPLATE_DATUM(WFaceExtractor); + } +} + +#endif // OPENPOSE__FACE__W_FACE_EXTRACTOR_HPP diff --git a/include/openpose/experimental/face/wFaceRenderer.hpp b/include/openpose/experimental/face/wFaceRenderer.hpp new file mode 100644 index 00000000..9d75ffd2 --- /dev/null +++ b/include/openpose/experimental/face/wFaceRenderer.hpp @@ -0,0 +1,88 @@ +#ifndef OPENPOSE__FACE__W_FACE_RENDERER_HPP +#define OPENPOSE__FACE__W_FACE_RENDERER_HPP + +#include // std::shared_ptr +#include "../../thread/worker.hpp" +#include "faceRenderer.hpp" + +namespace op +{ + namespace experimental + { + template + class WFaceRenderer : public Worker + { + public: + explicit WFaceRenderer(const std::shared_ptr& faceRenderer); + + void initializationOnThread(); + + void work(TDatums& tDatums); + + private: + std::shared_ptr spFaceRenderer; + + DELETE_COPY(WFaceRenderer); + }; + } +} + + + + + +// Implementation +#include "../../utilities/errorAndLog.hpp" +#include "../../utilities/macros.hpp" +#include "../../utilities/pointerContainer.hpp" +#include "../../utilities/profiler.hpp" +namespace op +{ + namespace experimental + { + template + WFaceRenderer::WFaceRenderer(const std::shared_ptr& faceRenderer) : + spFaceRenderer{faceRenderer} + { + } + + template + void WFaceRenderer::initializationOnThread() + { + spFaceRenderer->initializationOnThread(); + } + + template + void WFaceRenderer::work(TDatums& tDatums) + { + try + { + if (checkNoNullNorEmpty(tDatums)) + { + // Debugging log + dLog("", Priority::Low, __LINE__, __FUNCTION__, __FILE__); + // Profiling speed + const auto profilerKey = Profiler::timerInit(__LINE__, __FUNCTION__, __FILE__); + // Render people face + for (auto& tDatum : *tDatums) + spFaceRenderer->renderFace(tDatum.outputData, tDatum.faceKeyPoints); + // Profiling speed + Profiler::timerEnd(profilerKey); + Profiler::printAveragedTimeMsOnIterationX(profilerKey, __LINE__, __FUNCTION__, __FILE__, 1000); + // Debugging log + dLog("", Priority::Low, __LINE__, __FUNCTION__, __FILE__); + } + } + catch (const std::exception& e) + { + this->stop(); + tDatums = nullptr; + error(e.what(), __LINE__, __FUNCTION__, __FILE__); + } + } + + COMPILE_TEMPLATE_DATUM(WFaceRenderer); + } +} + +#endif // OPENPOSE__FACE__W_FACE_RENDERER_HPP diff --git a/include/openpose/experimental/filestream/wPoseJsonCocoSaver.hpp b/include/openpose/experimental/filestream/wPoseJsonCocoSaver.hpp index 9c75d921..73508ff5 100644 --- a/include/openpose/experimental/filestream/wPoseJsonCocoSaver.hpp +++ b/include/openpose/experimental/filestream/wPoseJsonCocoSaver.hpp @@ -78,7 +78,7 @@ namespace op if (imageId <= 50006) // 3559 images ~ 4 min at 15fps // move this to producer as --frame_last 50006 { // Record json in COCO format if file within desired range of images - spPoseJsonCocoSaver->record(tDatum.pose, imageId); + spPoseJsonCocoSaver->record(tDatum.poseKeyPoints, imageId); // Profiling speed Profiler::timerEnd(profilerKey); Profiler::printAveragedTimeMsOnIterationX(profilerKey, __LINE__, __FUNCTION__, __FILE__, 1000); diff --git a/include/openpose/experimental/hand/enumClasses.hpp b/include/openpose/experimental/hand/enumClasses.hpp new file mode 100644 index 00000000..3f77c73e --- /dev/null +++ b/include/openpose/experimental/hand/enumClasses.hpp @@ -0,0 +1,14 @@ +#ifndef OPENPOSE__HAND__ENUM_CLASSES_HPP +#define OPENPOSE__HAND__ENUM_CLASSES_HPP + +namespace op +{ + enum class HandsProperty : bool + { + NMSThreshold = 0, + Size, + }; +} + +#endif // OPENPOSE__HAND__ENUM_CLASSES_HPP + diff --git a/include/openpose/experimental/hands/handsExtractor.hpp b/include/openpose/experimental/hand/handExtractor.hpp similarity index 79% rename from include/openpose/experimental/hands/handsExtractor.hpp rename to include/openpose/experimental/hand/handExtractor.hpp index 587e8399..be5187af 100644 --- a/include/openpose/experimental/hands/handsExtractor.hpp +++ b/include/openpose/experimental/hand/handExtractor.hpp @@ -1,5 +1,5 @@ -#ifndef OPENPOSE__HANDS__HANDS_EXTRACTOR_HPP -#define OPENPOSE__HANDS__HANDS_EXTRACTOR_HPP +#ifndef OPENPOSE__HAND__HAND_EXTRACTOR_HPP +#define OPENPOSE__HAND__HAND_EXTRACTOR_HPP #include #include @@ -17,16 +17,16 @@ namespace op { namespace experimental { - class HandsExtractor + class HandExtractor { public: - explicit HandsExtractor(const std::string& modelFolder, const int gpuId, const PoseModel poseModel); + explicit HandExtractor(const std::string& modelFolder, const int gpuId, const PoseModel poseModel); void initializationOnThread(); - void forwardPass(const Array& pose, const cv::Mat& cvInputData); + void forwardPass(const Array& poseKeyPoints, const cv::Mat& cvInputData); - Array getHands() const; + Array getHandKeyPoints() const; double get(const HandsProperty property) const; @@ -60,9 +60,9 @@ namespace op void checkThread() const; - DELETE_COPY(HandsExtractor); + DELETE_COPY(HandExtractor); }; } } -#endif // OPENPOSE__HANDS__HANDS_EXTRACTOR_HPP +#endif // OPENPOSE__HAND__HAND_EXTRACTOR_HPP diff --git a/include/openpose/experimental/hand/handParameters.hpp b/include/openpose/experimental/hand/handParameters.hpp new file mode 100644 index 00000000..9b6a2a98 --- /dev/null +++ b/include/openpose/experimental/hand/handParameters.hpp @@ -0,0 +1,32 @@ +#ifndef OPENPOSE__HAND__HAND_PARAMETERS_HPP +#define OPENPOSE__HAND__HAND_PARAMETERS_HPP + +#include "enumClasses.hpp" +#include "../../pose/poseParameters.hpp" + +namespace op +{ + const unsigned char HAND_MAX_NUMBER_HANDS = 2; + + const unsigned char HAND_NUMBER_PARTS = 21; + #define HAND_PAIRS_TO_RENDER {0,1, 1,2, 2,3, 3,4, 0,5, 5,6, 6,7, 7,8, 0,9, 9,10, 10,11, 11,12, 0,13, 13,14, 14,15, 15,16, 0,17, 17,18, 18,19, 19,20} + + // Constant Global Parameters + // const unsigned char HAND_MAX_PEOPLE = 1; + + // Constant parameters + const auto HAND_CCN_DECREASE_FACTOR = 8.f; + const unsigned int HAND_MAX_PEAKS = 64u; + const std::string HAND_PROTOTXT{"hand/pose_deploy.prototxt"}; + const std::string HAND_TRAINED_MODEL{"hand/pose_iter_120000.caffemodel"}; + + // Default Model Parameters + // They might be modified on running time + const auto HAND_DEFAULT_NMS_THRESHOLD = 0.1f; + + // Rendering default parameters + const auto HAND_DEFAULT_ALPHA_HANDS = POSE_DEFAULT_ALPHA_POSE; + // const auto HAND_DEFAULT_ALPHA_HEATMAP = POSE_DEFAULT_ALPHA_HEATMAP; +} + +#endif // OPENPOSE__HAND__HAND_PARAMETERS_HPP diff --git a/include/openpose/experimental/hand/handRenderGpu.hpp b/include/openpose/experimental/hand/handRenderGpu.hpp new file mode 100644 index 00000000..9be8412f --- /dev/null +++ b/include/openpose/experimental/hand/handRenderGpu.hpp @@ -0,0 +1,12 @@ +#ifndef OPENPOSE__HAND__GPU_HAND_RENDER_HPP +#define OPENPOSE__HAND__GPU_HAND_RENDER_HPP + +#include +#include "handParameters.hpp" + +namespace op +{ + void renderHandsGpu(float* framePtr, const cv::Size& frameSize, const float* const handsPtr, const int numberHands, const float alphaColorToAdd = HAND_DEFAULT_ALPHA_HANDS); +} + +#endif // OPENPOSE__HAND__GPU_HAND_RENDER_HPP diff --git a/include/openpose/experimental/hands/handsRenderer.hpp b/include/openpose/experimental/hand/handRenderer.hpp similarity index 57% rename from include/openpose/experimental/hands/handsRenderer.hpp rename to include/openpose/experimental/hand/handRenderer.hpp index 4174a332..139513f8 100644 --- a/include/openpose/experimental/hands/handsRenderer.hpp +++ b/include/openpose/experimental/hand/handRenderer.hpp @@ -1,5 +1,5 @@ -#ifndef OPENPOSE__HANDS__HANDS_RENDERER_HPP -#define OPENPOSE__HANDS__HANDS_RENDERER_HPP +#ifndef OPENPOSE__HAND__HAND_RENDERER_HPP +#define OPENPOSE__HAND__HAND_RENDERER_HPP #include #include "../../core/array.hpp" @@ -10,24 +10,24 @@ namespace op { namespace experimental { - class HandsRenderer : public Renderer + class HandRenderer : public Renderer { public: - explicit HandsRenderer(const cv::Size& frameSize); + explicit HandRenderer(const cv::Size& frameSize); - ~HandsRenderer(); + ~HandRenderer(); void initializationOnThread(); - void renderHands(Array& outputData, const Array& hands); + void renderHands(Array& outputData, const Array& handKeyPoints); private: const cv::Size mFrameSize; float* pGpuHands; // GPU aux memory - DELETE_COPY(HandsRenderer); + DELETE_COPY(HandRenderer); }; } } -#endif // OPENPOSE__HANDS__HANDS_RENDERER_HPP +#endif // OPENPOSE__HAND__HAND_RENDERER_HPP diff --git a/include/openpose/experimental/hand/headers.hpp b/include/openpose/experimental/hand/headers.hpp new file mode 100644 index 00000000..74fe189e --- /dev/null +++ b/include/openpose/experimental/hand/headers.hpp @@ -0,0 +1,13 @@ +#ifndef OPENPOSE__HAND__HEADERS_HPP +#define OPENPOSE__HAND__HEADERS_HPP + +// hand module +#include "enumClasses.hpp" +#include "handExtractor.hpp" +#include "handParameters.hpp" +#include "handRenderer.hpp" +#include "handRenderGpu.hpp" +#include "wHandExtractor.hpp" +#include "wHandRenderer.hpp" + +#endif // OPENPOSE__HAND__HEADERS_HPP diff --git a/include/openpose/experimental/hands/wHandsExtractor.hpp b/include/openpose/experimental/hand/wHandExtractor.hpp similarity index 64% rename from include/openpose/experimental/hands/wHandsExtractor.hpp rename to include/openpose/experimental/hand/wHandExtractor.hpp index 5b99c75a..ee6e54e5 100644 --- a/include/openpose/experimental/hands/wHandsExtractor.hpp +++ b/include/openpose/experimental/hand/wHandExtractor.hpp @@ -1,28 +1,28 @@ -#ifndef OPENPOSE__HANDS__W_HANDS_EXTRACTOR_HPP -#define OPENPOSE__HANDS__W_HANDS_EXTRACTOR_HPP +#ifndef OPENPOSE__HAND__W_HAND_EXTRACTOR_HPP +#define OPENPOSE__HAND__W_HAND_EXTRACTOR_HPP #include // std::shared_ptr #include "../../thread/worker.hpp" -#include "handsRenderer.hpp" +#include "handRenderer.hpp" namespace op { namespace experimental { template - class WHandsExtractor : public Worker + class WHandExtractor : public Worker { public: - explicit WHandsExtractor(const std::shared_ptr& handsExtractor); + explicit WHandExtractor(const std::shared_ptr& handExtractor); void initializationOnThread(); void work(TDatums& tDatums); private: - std::shared_ptr spHandsExtractor; + std::shared_ptr spHandExtractor; - DELETE_COPY(WHandsExtractor); + DELETE_COPY(WHandExtractor); }; } } @@ -41,19 +41,19 @@ namespace op namespace experimental { template - WHandsExtractor::WHandsExtractor(const std::shared_ptr& handsExtractor) : - spHandsExtractor{handsExtractor} + WHandExtractor::WHandExtractor(const std::shared_ptr& handExtractor) : + spHandExtractor{handExtractor} { } template - void WHandsExtractor::initializationOnThread() + void WHandExtractor::initializationOnThread() { - spHandsExtractor->initializationOnThread(); + spHandExtractor->initializationOnThread(); } template - void WHandsExtractor::work(TDatums& tDatums) + void WHandExtractor::work(TDatums& tDatums) { try { @@ -66,8 +66,8 @@ namespace op // Extract people hands for (auto& tDatum : *tDatums) { - spHandsExtractor->forwardPass(tDatum.pose, tDatum.cvInputData); - tDatum.hands = spHandsExtractor->getHands(); + spHandExtractor->forwardPass(tDatum.poseKeyPoints, tDatum.cvInputData); + tDatum.handKeyPoints = spHandExtractor->getHandKeyPoints(); } // Profiling speed Profiler::timerEnd(profilerKey); @@ -84,8 +84,8 @@ namespace op } } - COMPILE_TEMPLATE_DATUM(WHandsExtractor); + COMPILE_TEMPLATE_DATUM(WHandExtractor); } } -#endif // OPENPOSE__HANDS__W_HANDS_EXTRACTOR_HPP +#endif // OPENPOSE__HAND__W_HAND_EXTRACTOR_HPP diff --git a/include/openpose/experimental/hands/wHandsRenderer.hpp b/include/openpose/experimental/hand/wHandRenderer.hpp similarity index 66% rename from include/openpose/experimental/hands/wHandsRenderer.hpp rename to include/openpose/experimental/hand/wHandRenderer.hpp index 25ff43cb..7ff7ddc6 100644 --- a/include/openpose/experimental/hands/wHandsRenderer.hpp +++ b/include/openpose/experimental/hand/wHandRenderer.hpp @@ -1,28 +1,28 @@ -#ifndef OPENPOSE__HANDS__W_HANDS_RENDERER_HPP -#define OPENPOSE__HANDS__W_HANDS_RENDERER_HPP +#ifndef OPENPOSE__HAND__W_HAND_RENDERER_HPP +#define OPENPOSE__HAND__W_HAND_RENDERER_HPP #include // std::shared_ptr #include "../../thread/worker.hpp" -#include "handsRenderer.hpp" +#include "handRenderer.hpp" namespace op { namespace experimental { template - class WHandsRenderer : public Worker + class WHandRenderer : public Worker { public: - explicit WHandsRenderer(const std::shared_ptr& handsRenderer); + explicit WHandRenderer(const std::shared_ptr& handRenderer); void initializationOnThread(); void work(TDatums& tDatums); private: - std::shared_ptr spHandsRenderer; + std::shared_ptr spHandRenderer; - DELETE_COPY(WHandsRenderer); + DELETE_COPY(WHandRenderer); }; } } @@ -41,19 +41,19 @@ namespace op namespace experimental { template - WHandsRenderer::WHandsRenderer(const std::shared_ptr& handsRenderer) : - spHandsRenderer{handsRenderer} + WHandRenderer::WHandRenderer(const std::shared_ptr& handRenderer) : + spHandRenderer{handRenderer} { } template - void WHandsRenderer::initializationOnThread() + void WHandRenderer::initializationOnThread() { - spHandsRenderer->initializationOnThread(); + spHandRenderer->initializationOnThread(); } template - void WHandsRenderer::work(TDatums& tDatums) + void WHandRenderer::work(TDatums& tDatums) { try { @@ -65,7 +65,7 @@ namespace op const auto profilerKey = Profiler::timerInit(__LINE__, __FUNCTION__, __FILE__); // Render people hands for (auto& tDatum : *tDatums) - spHandsRenderer->renderHands(tDatum.outputData, tDatum.hands); + spHandRenderer->renderHands(tDatum.outputData, tDatum.handKeyPoints); // Profiling speed Profiler::timerEnd(profilerKey); Profiler::printAveragedTimeMsOnIterationX(profilerKey, __LINE__, __FUNCTION__, __FILE__, 1000); @@ -81,8 +81,8 @@ namespace op } } - COMPILE_TEMPLATE_DATUM(WHandsRenderer); + COMPILE_TEMPLATE_DATUM(WHandRenderer); } } -#endif // OPENPOSE__HANDS__W_HANDS_RENDERER_HPP +#endif // OPENPOSE__HAND__W_HAND_RENDERER_HPP diff --git a/include/openpose/experimental/hands/enumClasses.hpp b/include/openpose/experimental/hands/enumClasses.hpp deleted file mode 100644 index 953073f8..00000000 --- a/include/openpose/experimental/hands/enumClasses.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef OPENPOSE__HANDS__ENUM_CLASSES_HPP -#define OPENPOSE__HANDS__ENUM_CLASSES_HPP - -namespace op -{ - enum class HandsProperty : bool - { - NMSThreshold = 0, - Size, - }; -} - -#endif // OPENPOSE__HANDS__ENUM_CLASSES_HPP - diff --git a/include/openpose/experimental/hands/handsParameters.hpp b/include/openpose/experimental/hands/handsParameters.hpp deleted file mode 100644 index 2828ae0e..00000000 --- a/include/openpose/experimental/hands/handsParameters.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef OPENPOSE__HANDS__HANDS_PARAMETERS_HPP -#define OPENPOSE__HANDS__HANDS_PARAMETERS_HPP - -#include "enumClasses.hpp" -#include "../../pose/poseParameters.hpp" - -namespace op -{ - const unsigned char HANDS_MAX_NUMBER_HANDS = 2; - - const unsigned char HANDS_NUMBER_PARTS = 21; - #define HANDS_PAIRS_TO_RENDER {0,1, 1,2, 2,3, 3,4, 0,5, 5,6, 6,7, 7,8, 0,9, 9,10, 10,11, 11,12, 0,13, 13,14, 14,15, 15,16, 0,17, 17,18, 18,19, 19,20} - - // Constant Global Parameters - // const unsigned char HANDS_MAX_PEOPLE = 1; - - // Constant parameters - const auto HANDS_CCN_DECREASE_FACTOR = 8.f; - const unsigned int HANDS_MAX_PEAKS = 64u; - const std::string HANDS_PROTOTXT{"hand/pose_deploy.prototxt"}; - const std::string HANDS_TRAINED_MODEL{"hand/pose_iter_120000.caffemodel"}; - - // Default Model Parameters - // They might be modified on running time - const auto HANDS_DEFAULT_NMS_THRESHOLD = 0.1f; - - // Rendering default parameters - const auto HANDS_DEFAULT_ALPHA_HANDS = POSE_DEFAULT_ALPHA_POSE; - // const auto HANDS_DEFAULT_ALPHA_HEATMAP = POSE_DEFAULT_ALPHA_HEATMAP; -} - -#endif // OPENPOSE__HANDS__HANDS_PARAMETERS_HPP diff --git a/include/openpose/experimental/hands/handsRenderGpu.hpp b/include/openpose/experimental/hands/handsRenderGpu.hpp deleted file mode 100644 index 7e686456..00000000 --- a/include/openpose/experimental/hands/handsRenderGpu.hpp +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef OPENPOSE__HANDS__GPU_HANDS_RENDER_HPP -#define OPENPOSE__HANDS__GPU_HANDS_RENDER_HPP - -#include -#include "handsParameters.hpp" - -namespace op -{ - void renderHandsGpu(float* framePtr, const cv::Size& frameSize, const float* const handsPtr, const int numberHands, const float alphaColorToAdd = HANDS_DEFAULT_ALPHA_HANDS); -} - -#endif // OPENPOSE__HANDS__GPU_HANDS_RENDER_HPP diff --git a/include/openpose/experimental/hands/headers.hpp b/include/openpose/experimental/hands/headers.hpp deleted file mode 100644 index c6a77c10..00000000 --- a/include/openpose/experimental/hands/headers.hpp +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef OPENPOSE__HANDS__HEADERS_HPP -#define OPENPOSE__HANDS__HEADERS_HPP - -// hands module -#include "enumClasses.hpp" -#include "handsExtractor.hpp" -#include "handsParameters.hpp" -#include "handsRenderer.hpp" -#include "handsRenderGpu.hpp" -#include "wHandsExtractor.hpp" -#include "wHandsRenderer.hpp" - -#endif // OPENPOSE__HANDS__HEADERS_HPP diff --git a/include/openpose/experimental/headers.hpp b/include/openpose/experimental/headers.hpp index 6a1f41b0..9cdbc5d8 100644 --- a/include/openpose/experimental/headers.hpp +++ b/include/openpose/experimental/headers.hpp @@ -1,11 +1,14 @@ #ifndef OPENPOSE__EXPERIMENTAL__HEADERS_HPP #define OPENPOSE__EXPERIMENTAL__HEADERS_HPP +// face module +#include "face/headers.hpp" + // filestream module #include "filestream/headers.hpp" // hands module -#include "hands/headers.hpp" +#include "hand/headers.hpp" // producer module // #include "producer/headers.hpp" diff --git a/include/openpose/filestream/poseJsonCocoSaver.hpp b/include/openpose/filestream/poseJsonCocoSaver.hpp index 61868a14..48af8e50 100644 --- a/include/openpose/filestream/poseJsonCocoSaver.hpp +++ b/include/openpose/filestream/poseJsonCocoSaver.hpp @@ -22,7 +22,7 @@ namespace op ~PoseJsonCocoSaver(); - void record(const Array& pose, const int imageId); + void record(const Array& poseKeyPoints, const int imageId); private: JsonOfstream mJsonOfstream; diff --git a/include/openpose/filestream/poseJsonSaver.hpp b/include/openpose/filestream/poseJsonSaver.hpp index e9c86b35..b09d12d2 100644 --- a/include/openpose/filestream/poseJsonSaver.hpp +++ b/include/openpose/filestream/poseJsonSaver.hpp @@ -13,7 +13,7 @@ namespace op public: explicit PoseJsonSaver(const std::string& directoryPath); - void savePoseVector(const std::vector>& poseVector, const std::string& fileName) const; + void savePoseKeyPoints(const std::vector>& poseKeyPointsVector, const std::string& fileName) const; }; } diff --git a/include/openpose/filestream/poseSaver.hpp b/include/openpose/filestream/poseSaver.hpp index 7aaaad28..28181721 100644 --- a/include/openpose/filestream/poseSaver.hpp +++ b/include/openpose/filestream/poseSaver.hpp @@ -12,7 +12,7 @@ namespace op public: PoseSaver(const std::string& directoryPath, const DataFormat format); - void savePoseVector(const std::vector>& poseVector, const std::string& fileName) const; + void savePoseKeyPoints(const std::vector>& poseKeyPointsVector, const std::string& fileName) const; private: const DataFormat mFormat; diff --git a/include/openpose/filestream/wPoseJsonSaver.hpp b/include/openpose/filestream/wPoseJsonSaver.hpp index 03564f0c..0c3e76c8 100644 --- a/include/openpose/filestream/wPoseJsonSaver.hpp +++ b/include/openpose/filestream/wPoseJsonSaver.hpp @@ -61,11 +61,11 @@ namespace op // T* to T auto& tDatumsNoPtr = *tDatums; // Record people pose data in json format - std::vector> poseVector(tDatumsNoPtr.size()); + std::vector> poseKeyPointsVector(tDatumsNoPtr.size()); for (auto i = 0; i < tDatumsNoPtr.size(); i++) - poseVector[i] = tDatumsNoPtr[i].pose; + poseKeyPointsVector[i] = tDatumsNoPtr[i].poseKeyPoints; const auto fileName = (!tDatumsNoPtr[0].name.empty() ? tDatumsNoPtr[0].name : std::to_string(tDatumsNoPtr[0].id)); - spPoseJsonSaver->savePoseVector(poseVector, fileName); + spPoseJsonSaver->savePoseKeyPoints(poseKeyPointsVector, fileName); // Profiling speed Profiler::timerEnd(profilerKey); Profiler::printAveragedTimeMsOnIterationX(profilerKey, __LINE__, __FUNCTION__, __FILE__, 1000); diff --git a/include/openpose/filestream/wPoseSaver.hpp b/include/openpose/filestream/wPoseSaver.hpp index bb5d427c..e1e375e0 100644 --- a/include/openpose/filestream/wPoseSaver.hpp +++ b/include/openpose/filestream/wPoseSaver.hpp @@ -62,11 +62,11 @@ namespace op // T* to T auto& tDatumsNoPtr = *tDatums; // Record people pose data - std::vector> poseVector(tDatumsNoPtr.size()); + std::vector> poseKeyPointsVector(tDatumsNoPtr.size()); for (auto i = 0; i < tDatumsNoPtr.size(); i++) - poseVector[i] = tDatumsNoPtr[i].pose; + poseKeyPointsVector[i] = tDatumsNoPtr[i].poseKeyPoints; const auto fileName = (!tDatumsNoPtr[0].name.empty() ? tDatumsNoPtr[0].name : std::to_string(tDatumsNoPtr[0].id)); - spPoseSaver->savePoseVector(poseVector, fileName); + spPoseSaver->savePoseKeyPoints(poseKeyPointsVector, fileName); // Profiling speed Profiler::timerEnd(profilerKey); Profiler::printAveragedTimeMsOnIterationX(profilerKey, __LINE__, __FUNCTION__, __FILE__, 1000); diff --git a/include/openpose/gui/guiInfoAdder.hpp b/include/openpose/gui/guiInfoAdder.hpp index b60c2bf7..15df926b 100644 --- a/include/openpose/gui/guiInfoAdder.hpp +++ b/include/openpose/gui/guiInfoAdder.hpp @@ -12,7 +12,7 @@ namespace op public: GuiInfoAdder(const cv::Size& outputSize, const int numberGpus); - void addInfo(cv::Mat& cvOutputData, const Array& pose, const unsigned long long id, const std::string& elementRenderedName); + void addInfo(cv::Mat& cvOutputData, const Array& poseKeyPoints, const unsigned long long id, const std::string& elementRenderedName); private: // Const variables diff --git a/include/openpose/gui/wGuiInfoAdder.hpp b/include/openpose/gui/wGuiInfoAdder.hpp index d3f73305..44b2f2b5 100644 --- a/include/openpose/gui/wGuiInfoAdder.hpp +++ b/include/openpose/gui/wGuiInfoAdder.hpp @@ -58,7 +58,7 @@ namespace op const auto profilerKey = Profiler::timerInit(__LINE__, __FUNCTION__, __FILE__); // Add GUI components to frame for (auto& tDatum : *tDatums) - spGuiInfoAdder->addInfo(tDatum.cvOutputData, tDatum.pose, tDatum.id, tDatum.elementRendered.second); + spGuiInfoAdder->addInfo(tDatum.cvOutputData, tDatum.poseKeyPoints, tDatum.id, tDatum.elementRendered.second); // Profiling speed Profiler::timerEnd(profilerKey); Profiler::printAveragedTimeMsOnIterationX(profilerKey, __LINE__, __FUNCTION__, __FILE__, 1000); diff --git a/include/openpose/pose/bodyPartConnectorBase.hpp b/include/openpose/pose/bodyPartConnectorBase.hpp index f3acffb1..1296a54b 100644 --- a/include/openpose/pose/bodyPartConnectorBase.hpp +++ b/include/openpose/pose/bodyPartConnectorBase.hpp @@ -7,11 +7,11 @@ namespace op { template - void connectBodyPartsCpu(Array& pose, const T* const heatMapPtr, const T* const peaksPtr, const PoseModel poseModel, const cv::Size& heatMapSize, const int maxPeaks, + void connectBodyPartsCpu(Array& poseKeyPoints, const T* const heatMapPtr, const T* const peaksPtr, const PoseModel poseModel, const cv::Size& heatMapSize, const int maxPeaks, const int interMinAboveThreshold, const T interThreshold, const int minSubsetCnt, const T minSubsetScore, const T scaleFactor = 1.f); template - void connectBodyPartsGpu(Array& pose, T* posePtr, const T* const heatMapPtr, const T* const peaksPtr, const PoseModel poseModel, const cv::Size& heatMapSize, + void connectBodyPartsGpu(Array& poseKeyPoints, T* posePtr, const T* const heatMapPtr, const T* const peaksPtr, const PoseModel poseModel, const cv::Size& heatMapSize, const int maxPeaks, const int interMinAboveThreshold, const T interThreshold, const int minSubsetCnt, const T minSubsetScore, const T scaleFactor = 1.f); } diff --git a/include/openpose/pose/bodyPartConnectorCaffe.hpp b/include/openpose/pose/bodyPartConnectorCaffe.hpp index f8d43991..d5d0856b 100644 --- a/include/openpose/pose/bodyPartConnectorCaffe.hpp +++ b/include/openpose/pose/bodyPartConnectorCaffe.hpp @@ -36,9 +36,9 @@ namespace op void setScaleNetToOutput(const T scaleNetToOutput); - virtual void Forward_cpu(const std::vector*>& bottom, Array& pose); + virtual void Forward_cpu(const std::vector*>& bottom, Array& poseKeyPoints); - virtual void Forward_gpu(const std::vector*>& bottom, const std::vector*>& top, Array& pose); + virtual void Forward_gpu(const std::vector*>& bottom, const std::vector*>& top, Array& poseKeyPoints); virtual void Backward_cpu(const std::vector*>& top, const std::vector& propagate_down, const std::vector*>& bottom); diff --git a/include/openpose/pose/poseExtractor.hpp b/include/openpose/pose/poseExtractor.hpp index e4d8f27c..1830b907 100644 --- a/include/openpose/pose/poseExtractor.hpp +++ b/include/openpose/pose/poseExtractor.hpp @@ -30,7 +30,7 @@ namespace op virtual const float* getPoseGpuConstPtr() const = 0; - Array getPose() const; + Array getPoseKeyPoints() const; double getScaleNetToOutput() const; @@ -44,7 +44,7 @@ namespace op const PoseModel mPoseModel; const cv::Size mNetOutputSize; const cv::Size mOutputSize; - Array mPose; + Array mPoseKeyPoints; double mScaleNetToOutput; void checkThread() const; diff --git a/include/openpose/pose/poseParameters.hpp b/include/openpose/pose/poseParameters.hpp index 438cd65c..4f592350 100644 --- a/include/openpose/pose/poseParameters.hpp +++ b/include/openpose/pose/poseParameters.hpp @@ -87,6 +87,10 @@ namespace op // Rendering default parameters const auto POSE_DEFAULT_ALPHA_POSE = 0.6f; const auto POSE_DEFAULT_ALPHA_HEATMAP = 0.7f; + + // Auxiliary functions + unsigned char poseBodyPartMapStringToKey(const PoseModel poseModel, const std::string& string); + unsigned char poseBodyPartMapStringToKey(const PoseModel poseModel, const std::vector& strings); } #endif // OPENPOSE__POSE__POSE_PARAMETERS_HPP diff --git a/include/openpose/pose/poseRenderer.hpp b/include/openpose/pose/poseRenderer.hpp index 96ba9eb9..3783e8cf 100644 --- a/include/openpose/pose/poseRenderer.hpp +++ b/include/openpose/pose/poseRenderer.hpp @@ -42,7 +42,7 @@ namespace op void setShowGooglyEyes(const bool showGooglyEyes); - std::pair renderPose(Array& outputData, const Array& pose, const double scaleNetToOutput = -1.); + std::pair renderPose(Array& outputData, const Array& poseKeyPoints, const double scaleNetToOutput = -1.); private: const cv::Size mHeatMapsSize; diff --git a/include/openpose/pose/wPoseExtractor.hpp b/include/openpose/pose/wPoseExtractor.hpp index b0947e12..f32d6b7b 100644 --- a/include/openpose/pose/wPoseExtractor.hpp +++ b/include/openpose/pose/wPoseExtractor.hpp @@ -63,7 +63,7 @@ namespace op { spPoseExtractor->forwardPass(tDatum.inputNetData, tDatum.cvInputData.size()); tDatum.poseHeatMaps = spPoseExtractor->getHeatMaps(); - tDatum.pose = spPoseExtractor->getPose(); + tDatum.poseKeyPoints = spPoseExtractor->getPoseKeyPoints(); tDatum.scaleNetToOutput = spPoseExtractor->getScaleNetToOutput(); } // Profiling speed diff --git a/include/openpose/pose/wPoseRenderer.hpp b/include/openpose/pose/wPoseRenderer.hpp index 11fe480d..f95336e9 100644 --- a/include/openpose/pose/wPoseRenderer.hpp +++ b/include/openpose/pose/wPoseRenderer.hpp @@ -60,7 +60,7 @@ namespace op const auto profilerKey = Profiler::timerInit(__LINE__, __FUNCTION__, __FILE__); // Render people pose for (auto& tDatum : *tDatums) - tDatum.elementRendered = spPoseRenderer->renderPose(tDatum.outputData, tDatum.pose, tDatum.scaleNetToOutput); + tDatum.elementRendered = spPoseRenderer->renderPose(tDatum.outputData, tDatum.poseKeyPoints, tDatum.scaleNetToOutput); // Profiling speed Profiler::timerEnd(profilerKey); Profiler::printAveragedTimeMsOnIterationX(profilerKey, __LINE__, __FUNCTION__, __FILE__, 1000); diff --git a/include/openpose/utilities/cuda.hu b/include/openpose/utilities/cuda.hu index 61ad0c4f..146e5fdc 100644 --- a/include/openpose/utilities/cuda.hu +++ b/include/openpose/utilities/cuda.hu @@ -140,7 +140,7 @@ namespace op colorR = addWeighted(colorR, colorToAdd[0], alphaColorToAdd); colorG = addWeighted(colorG, colorToAdd[1], alphaColorToAdd); colorB = addWeighted(colorB, colorToAdd[2], alphaColorToAdd); - } + } } #endif // OPENPOSE__UTILITIES_CUDA_HU diff --git a/include/openpose/utilities/render.hu b/include/openpose/utilities/render.hu new file mode 100644 index 00000000..87d18299 --- /dev/null +++ b/include/openpose/utilities/render.hu @@ -0,0 +1,120 @@ +#ifndef OPENPOSE__UTILITIES_RENDER_HU +#define OPENPOSE__UTILITIES_RENDER_HU + +namespace op +{ + inline __device__ void renderKeyPoints(float* targetPtr, float2* sharedMaxs, float2* sharedMins, float* sharedScaleF, + const int globalIdx, const int x, const int y, const int targetWidth, const int targetHeight, + const float* const facePtr, const unsigned char* const partPairsPtr, + const int numberFaces, const int numberParts, const int numberPartPairs, + const float* const rgbColorsPtr, const int numberColors, + const float radius, const float stickwidth, const float threshold, const float alphaColorToAdd) + { + // Fill shared parameters + if (globalIdx < numberFaces) + { + sharedMins[globalIdx].x = targetWidth; + sharedMins[globalIdx].y = targetHeight; + sharedMaxs[globalIdx].x = 0.f; + sharedMaxs[globalIdx].y = 0.f; + for (auto part = 0 ; part < numberParts ; part++) + { + const auto index = 3 * (globalIdx*numberParts + part); + const auto x = facePtr[index]; + const auto y = facePtr[index+1]; + const auto score = facePtr[index+2]; + if (score > threshold) + { + if (x < sharedMins[globalIdx].x) + sharedMins[globalIdx].x = x; + if (x > sharedMaxs[globalIdx].x) + sharedMaxs[globalIdx].x = x; + if (y < sharedMins[globalIdx].y) + sharedMins[globalIdx].y = y; + if (y > sharedMaxs[globalIdx].y) + sharedMaxs[globalIdx].y = y; + } + } + const auto averageX = sharedMaxs[globalIdx].x-sharedMins[globalIdx].x; + const auto averageY = sharedMaxs[globalIdx].y-sharedMins[globalIdx].y; + sharedScaleF[globalIdx] = fastTruncate((averageX + averageY) / 400.f, 0.33f, 1.f); // (averageX + averageY) / 2.f / 400.f + const auto constantToAdd = 50.f; + sharedMaxs[globalIdx].x += constantToAdd; + sharedMaxs[globalIdx].y += constantToAdd; + sharedMins[globalIdx].x -= constantToAdd; + sharedMins[globalIdx].y -= constantToAdd; + } + + __syncthreads(); + + // Fill each (x,y) target pixel + if (x < targetWidth && y < targetHeight) + { + const auto baseIndex = y * targetWidth + x; + auto& b = targetPtr[ baseIndex]; + auto& g = targetPtr[ targetWidth * targetHeight + baseIndex]; + auto& r = targetPtr[2 * targetWidth * targetHeight + baseIndex]; + + for (auto person = 0; person < numberFaces; person++) + { + if (x <= sharedMaxs[person].x && x >= sharedMins[person].x && y <= sharedMaxs[person].y && y >= sharedMins[person].y) + { + // Hand part connections + for (auto facePart = 0; facePart < numberPartPairs; facePart++) + { + const auto bSqrt = sharedScaleF[person] * sharedScaleF[person] * stickwidth * stickwidth; + const auto partA = partPairsPtr[2*facePart]; + const auto partB = partPairsPtr[2*facePart+1]; + const auto indexA = person*numberParts*3 + partA*3; + const auto xA = facePtr[indexA]; + const auto yA = facePtr[indexA + 1]; + const auto scoreA = facePtr[indexA + 2]; + const auto indexB = person*numberParts*3 + partB*3; + const auto xB = facePtr[indexB]; + const auto yB = facePtr[indexB + 1]; + const auto scoreB = facePtr[indexB + 2]; + + if (scoreA > threshold && scoreB > threshold) + { + const auto xP = (xA + xB) / 2.f; + const auto yP = (yA + yB) / 2.f; + const auto aSqrt = (xA - xP) * (xA - xP) + (yA - yP) * (yA - yP); + + const auto angle = atan2f(yB - yA, xB - xA); + const auto sine = sinf(angle); + const auto cosine = cosf(angle); + const auto A = cosine * (x - xP) + sine * (y - yP); + const auto B = sine * (x - xP) - cosine * (y - yP); + + const auto judge = A * A / aSqrt + B * B / bSqrt; + const auto minV = 0.f; + const auto maxV = 1.f; + if (minV <= judge && judge <= maxV) + addColorWeighted(r, g, b, &rgbColorsPtr[(facePart%numberColors)*3], alphaColorToAdd); + } + } + + // Hand part circles + for (unsigned char i = 0; i < numberParts; i++) + { + const auto index = 3 * (person*numberParts + i); + const auto localX = facePtr[index]; + const auto localY = facePtr[index + 1]; + const auto score = facePtr[index + 2]; + + if (score > threshold) + { + const auto dist2 = (x - localX) * (x - localX) + (y - localY) * (y - localY); + const auto minr2 = 0.f; + const auto maxr2 = sharedScaleF[person]*sharedScaleF[person]*radius * radius; + if (minr2 <= dist2 && dist2 <= maxr2) + addColorWeighted(r, g, b, &rgbColorsPtr[(i%numberColors)*3], alphaColorToAdd); + } + } + } + } + } + } +} + +#endif // OPENPOSE__UTILITIES_RENDER_HU diff --git a/include/openpose/wrapper/headers.hpp b/include/openpose/wrapper/headers.hpp index 528a9ab0..80ff7a56 100644 --- a/include/openpose/wrapper/headers.hpp +++ b/include/openpose/wrapper/headers.hpp @@ -3,7 +3,8 @@ // wrapper module #include "wrapper.hpp" -#include "wrapperStructHands.hpp" +#include "wrapperStructFace.hpp" +#include "wrapperStructHand.hpp" #include "wrapperStructInput.hpp" #include "wrapperStructOutput.hpp" #include "wrapperStructPose.hpp" diff --git a/include/openpose/wrapper/wrapper.hpp b/include/openpose/wrapper/wrapper.hpp index 190cf2ec..a2cbfc0d 100644 --- a/include/openpose/wrapper/wrapper.hpp +++ b/include/openpose/wrapper/wrapper.hpp @@ -2,10 +2,11 @@ #define OPENPOSE__WRAPPER__WRAPPER_HPP #include "../thread/headers.hpp" -#include "wrapperStructPose.hpp" -#include "wrapperStructHands.hpp" +#include "wrapperStructFace.hpp" +#include "wrapperStructHand.hpp" #include "wrapperStructInput.hpp" #include "wrapperStructOutput.hpp" +#include "wrapperStructPose.hpp" namespace op { @@ -72,18 +73,40 @@ namespace op */ 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. + // 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{}, + const WrapperStructInput& wrapperStructInput, // Consumer (keep default values to disable any output) const WrapperStructOutput& wrapperStructOutput = WrapperStructOutput{}); + // THIS FUNCTION IS NOT IMPLEMENTED YET -> COMING SOON + // Similar to the previos configure, but it includes hand extraction and rendering + void configure(const WrapperStructPose& wrapperStructPose, + // Hand (use the default WrapperStructHand{} to disable any hand detector) + const experimental::WrapperStructHand& wrapperHandStruct, + // Producer (set producerSharedPtr = nullptr or use the default WrapperStructInput{} to disable any input) + const WrapperStructInput& wrapperStructInput, + // Consumer (keep default values to disable any output) + const WrapperStructOutput& wrapperStructOutput = WrapperStructOutput{}); + + // THIS FUNCTION IS NOT IMPLEMENTED YET -> COMING SOON + // Similar to the previos configure, but it includes hand extraction and rendering + void configure(const WrapperStructPose& wrapperStructPose, + // Face (use the default WrapperStructFace{} to disable any face detector) + const experimental::WrapperStructFace& wrapperStructFace, + // Producer (set producerSharedPtr = nullptr or use the default WrapperStructInput{} to disable any input) + const WrapperStructInput& wrapperStructInput, + // Consumer (keep default values to disable any output) + const WrapperStructOutput& wrapperStructOutput = WrapperStructOutput{}); + + // THIS FUNCTION IS NOT IMPLEMENTED YET -> COMING SOON // Similar to the previos configure, but it includes hand extraction and rendering void configure(const WrapperStructPose& wrapperStructPose = WrapperStructPose{}, - // Hand (use the default WrapperStructHands{} to disable any hand detector) - const experimental::WrapperStructHands& wrapperHandStruct = experimental::WrapperStructHands{}, + // Face (use the default WrapperStructFace{} to disable any face detector) + const experimental::WrapperStructFace& wrapperStructFace = experimental::WrapperStructFace{}, + // Hand (use the default WrapperStructHand{} to disable any hand detector) + const experimental::WrapperStructHand& wrapperHandStruct = experimental::WrapperStructHand{}, // 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) @@ -349,7 +372,7 @@ namespace op { try { - configure(wrapperStructPose, experimental::WrapperStructHands{}, wrapperStructInput, wrapperStructOutput); + configure(wrapperStructPose, experimental::WrapperStructFace{}, experimental::WrapperStructHand{}, wrapperStructInput, wrapperStructOutput); } catch (const std::exception& e) { @@ -358,8 +381,37 @@ namespace op } template - void Wrapper::configure(const WrapperStructPose& wrapperStructPose, const experimental::WrapperStructHands& wrapperHandStruct, + void Wrapper::configure(const WrapperStructPose& wrapperStructPose, const experimental::WrapperStructFace& wrapperStructFace, const WrapperStructInput& wrapperStructInput, const WrapperStructOutput& wrapperStructOutput) + { + try + { + configure(wrapperStructPose, wrapperStructFace, experimental::WrapperStructHand{}, wrapperStructInput, wrapperStructOutput); + } + catch (const std::exception& e) + { + error(e.what(), __LINE__, __FUNCTION__, __FILE__); + } + } + + template + void Wrapper::configure(const WrapperStructPose& wrapperStructPose, const experimental::WrapperStructHand& wrapperHandStruct, + const WrapperStructInput& wrapperStructInput, const WrapperStructOutput& wrapperStructOutput) + { + try + { + configure(wrapperStructPose, experimental::WrapperStructFace{}, wrapperHandStruct, wrapperStructInput, wrapperStructOutput); + } + catch (const std::exception& e) + { + error(e.what(), __LINE__, __FUNCTION__, __FILE__); + } + } + + template + void Wrapper::configure(const WrapperStructPose& wrapperStructPose, const experimental::WrapperStructFace& wrapperStructFace, + const experimental::WrapperStructHand& wrapperHandStruct, const WrapperStructInput& wrapperStructInput, + const WrapperStructOutput& wrapperStructOutput) { try { @@ -393,8 +445,8 @@ namespace op } 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 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()); @@ -516,15 +568,27 @@ namespace op for (auto i = 0; i < spWPoses.size(); i++) spWPoses.at(i) = {std::make_shared>(poseExtractors.at(i))}; + // Face extractor(s) + if (wrapperStructFace.extractAndRenderFace) + { + for (auto gpuId = 0; gpuId < spWPoses.size(); gpuId++) + { + const auto faceExtractor = std::make_shared( + wrapperStructPose.modelFolder, gpuId + wrapperStructPose.gpuNumberStart, wrapperStructPose.poseModel + ); + spWPoses.at(gpuId).emplace_back(std::make_shared>(faceExtractor)); + } + } + // Hand extractor(s) if (wrapperHandStruct.extractAndRenderHands) { for (auto gpuId = 0; gpuId < spWPoses.size(); gpuId++) { - const auto handsExtractor = std::make_shared( + const auto handExtractor = std::make_shared( wrapperStructPose.modelFolder, gpuId + wrapperStructPose.gpuNumberStart, wrapperStructPose.poseModel ); - spWPoses.at(gpuId).emplace_back(std::make_shared>(handsExtractor)); + spWPoses.at(gpuId).emplace_back(std::make_shared>(handExtractor)); } } @@ -539,15 +603,33 @@ namespace op for (auto i = 0; i < spWPoses.size(); i++) { // Construct hands renderer - const auto handsRenderer = std::make_shared(finalOutputSize); + const auto handRenderer = std::make_shared(finalOutputSize); + // Performance boost -> share spGpuMemoryPtr for all renderers + if (!poseRenderers.empty()) + { + const bool isLastRenderer = (!wrapperStructFace.extractAndRenderFace); + handRenderer->setGpuMemoryAndSetIfLast(poseRenderers.at(i)->getGpuMemoryAndSetAsFirst(), isLastRenderer); + } + // Add worker + spWPoses.at(i).emplace_back(std::make_shared>(handRenderer)); + } + } + + // Face renderer(s) + if (wrapperStructFace.extractAndRenderFace) + { + for (auto i = 0; i < spWPoses.size(); i++) + { + // Construct face renderer + const auto faceRenderer = std::make_shared(finalOutputSize); // Performance boost -> share spGpuMemoryPtr for all renderers if (!poseRenderers.empty()) { const bool isLastRenderer = true; - handsRenderer->setGpuMemoryAndSetIfLast(poseRenderers.at(i)->getGpuMemoryAndSetAsFirst(), isLastRenderer); + faceRenderer->setGpuMemoryAndSetIfLast(poseRenderers.at(i)->getGpuMemoryAndSetAsFirst(), isLastRenderer); } // Add worker - spWPoses.at(i).emplace_back(std::make_shared>(handsRenderer)); + spWPoses.at(i).emplace_back(std::make_shared>(faceRenderer)); } } @@ -567,8 +649,8 @@ namespace op && (wrapperStructPose.poseScaleMode != ScaleMode::InputResolution || (finalOutputSize != producerSize)) && (wrapperStructPose.poseScaleMode != ScaleMode::NetOutputResolution || (finalOutputSize != netOutputSize))) { - auto arrayScaler = std::make_shared(wrapperStructPose.poseScaleMode); - mPostProcessingWs.emplace_back(std::make_shared>(arrayScaler)); + auto keyPointScaler = std::make_shared(wrapperStructPose.poseScaleMode); + mPostProcessingWs.emplace_back(std::make_shared>(keyPointScaler)); } mOutputWs.clear(); diff --git a/include/openpose/wrapper/wrapperStructFace.hpp b/include/openpose/wrapper/wrapperStructFace.hpp new file mode 100644 index 00000000..f2c84712 --- /dev/null +++ b/include/openpose/wrapper/wrapperStructFace.hpp @@ -0,0 +1,32 @@ +#ifndef OPENPOSE__WRAPPER__WRAPPER_STRUCT_FACE_HPP +#define OPENPOSE__WRAPPER__WRAPPER_STRUCT_FACE_HPP + +namespace op +{ + namespace experimental + { + /** + * WrapperStructFace: Face estimation and rendering configuration struct. + * DO NOT USE. CODE TO BE FINISHED. + * WrapperStructFace allows the user to set up the face estimation and rendering parameters that will be used for the OpenPose Wrapper + * class. + */ + struct WrapperStructFace + { + /** + * PROVISIONAL PARAMETER. IT WILL BE CHANGED. + * Whether to extract and render face. + */ + bool extractAndRenderFace; + + /** + * 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. + */ + WrapperStructFace(const bool extractAndRenderFace = false); + }; + } +} + +#endif // OPENPOSE__WRAPPER__WRAPPER_STRUCT_FACE_HPP diff --git a/include/openpose/wrapper/wrapperStructHands.hpp b/include/openpose/wrapper/wrapperStructHand.hpp similarity index 55% rename from include/openpose/wrapper/wrapperStructHands.hpp rename to include/openpose/wrapper/wrapperStructHand.hpp index 20a95ed2..998e86e4 100644 --- a/include/openpose/wrapper/wrapperStructHands.hpp +++ b/include/openpose/wrapper/wrapperStructHand.hpp @@ -1,17 +1,17 @@ -#ifndef OPENPOSE__WRAPPER__WRAPPER_STRUCT_HANDS_HPP -#define OPENPOSE__WRAPPER__WRAPPER_STRUCT_HANDS_HPP +#ifndef OPENPOSE__WRAPPER__WRAPPER_STRUCT_HAND_HPP +#define OPENPOSE__WRAPPER__WRAPPER_STRUCT_HAND_HPP namespace op { namespace experimental { /** - * WrapperStructHands: Hands estimation and rendering configuration struct. + * WrapperStructHand: 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 + * WrapperStructHand allows the user to set up the hands estimation and rendering parameters that will be used for the OpenPose Wrapper * class. */ - struct WrapperStructHands + struct WrapperStructHand { /** * PROVISIONAL PARAMETER. IT WILL BE CHANGED. @@ -24,9 +24,9 @@ namespace op * 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); + WrapperStructHand(const bool extractAndRenderHands = false); }; } } -#endif // OPENPOSE__WRAPPER__WRAPPER_STRUCT_HANDS_HPP +#endif // OPENPOSE__WRAPPER__WRAPPER_STRUCT_HAND_HPP diff --git a/include/openpose/wrapper/wrapperStructPose.hpp b/include/openpose/wrapper/wrapperStructPose.hpp index 9c389800..9dd7d505 100644 --- a/include/openpose/wrapper/wrapperStructPose.hpp +++ b/include/openpose/wrapper/wrapperStructPose.hpp @@ -25,14 +25,14 @@ namespace op /** * 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 + * The final Datum.poseKeyPoints 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), + * Final scale of the Array Datum.poseKeyPoints and the writen pose data. + * The final Datum.poseKeyPoints 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; diff --git a/src/openpose/core/datum.cpp b/src/openpose/core/datum.cpp index 450537a1..81e968d5 100644 --- a/src/openpose/core/datum.cpp +++ b/src/openpose/core/datum.cpp @@ -18,9 +18,10 @@ namespace op outputData{datum.outputData}, cvOutputData{datum.cvOutputData}, // Resulting Array data - pose{datum.pose}, + poseKeyPoints{datum.poseKeyPoints}, poseHeatMaps{datum.poseHeatMaps}, - hands{datum.hands}, + faceKeyPoints{datum.faceKeyPoints}, + handKeyPoints{datum.handKeyPoints}, // Other parameters scaleInputToOutput{datum.scaleInputToOutput}, scaleNetToOutput{datum.scaleNetToOutput}, @@ -42,9 +43,10 @@ namespace op outputData = datum.outputData; cvOutputData = datum.cvOutputData; // Resulting Array data - pose = datum.pose; + poseKeyPoints = datum.poseKeyPoints; poseHeatMaps = datum.poseHeatMaps, - hands = datum.hands, + faceKeyPoints = datum.faceKeyPoints, + handKeyPoints = datum.handKeyPoints, // Other parameters scaleInputToOutput = datum.scaleInputToOutput; scaleNetToOutput = datum.scaleNetToOutput; @@ -77,9 +79,10 @@ namespace op std::swap(outputData, datum.outputData); std::swap(cvOutputData, datum.cvOutputData); // Resulting Array data - std::swap(pose, datum.pose); + std::swap(poseKeyPoints, datum.poseKeyPoints); std::swap(poseHeatMaps, datum.poseHeatMaps); - std::swap(hands, datum.hands); + std::swap(faceKeyPoints, datum.faceKeyPoints); + std::swap(handKeyPoints, datum.handKeyPoints); // Other parameters std::swap(elementRendered, datum.elementRendered); } @@ -103,9 +106,10 @@ namespace op std::swap(outputData, datum.outputData); std::swap(cvOutputData, datum.cvOutputData); // Resulting Array data - std::swap(pose, datum.pose); + std::swap(poseKeyPoints, datum.poseKeyPoints); std::swap(poseHeatMaps, datum.poseHeatMaps); - std::swap(hands, datum.hands); + std::swap(faceKeyPoints, datum.faceKeyPoints); + std::swap(handKeyPoints, datum.handKeyPoints); // Other parameters scaleInputToOutput = datum.scaleInputToOutput; scaleNetToOutput = datum.scaleNetToOutput; @@ -139,9 +143,10 @@ namespace op datum.outputData = outputData.clone(); datum.cvOutputData = cvOutputData.clone(); // Resulting Array data - datum.pose = pose.clone(); + datum.poseKeyPoints = poseKeyPoints.clone(); datum.poseHeatMaps = poseHeatMaps.clone(); - datum.hands = hands.clone(); + datum.faceKeyPoints = faceKeyPoints.clone(); + datum.handKeyPoints = handKeyPoints.clone(); // Other parameters datum.scaleInputToOutput = scaleInputToOutput; datum.scaleNetToOutput = scaleNetToOutput; diff --git a/src/openpose/core/defineTemplates.cpp b/src/openpose/core/defineTemplates.cpp index 2bd1e2a1..d3decfa8 100644 --- a/src/openpose/core/defineTemplates.cpp +++ b/src/openpose/core/defineTemplates.cpp @@ -2,8 +2,8 @@ namespace op { - DEFINE_TEMPLATE_DATUM(WArrayScaler); DEFINE_TEMPLATE_DATUM(WCvMatToOpInput); DEFINE_TEMPLATE_DATUM(WCvMatToOpOutput); + DEFINE_TEMPLATE_DATUM(WKeyPointScaler); DEFINE_TEMPLATE_DATUM(WOpOutputToCvMat); } diff --git a/src/openpose/core/arrayScaler.cpp b/src/openpose/core/keyPointScaler.cpp similarity index 55% rename from src/openpose/core/arrayScaler.cpp rename to src/openpose/core/keyPointScaler.cpp index 72f094d4..e5664aad 100644 --- a/src/openpose/core/arrayScaler.cpp +++ b/src/openpose/core/keyPointScaler.cpp @@ -1,20 +1,20 @@ -#include "openpose/core/scalePose.hpp" +#include "openpose/core/scaleKeyPoints.hpp" #include "openpose/utilities/errorAndLog.hpp" -#include "openpose/core/arrayScaler.hpp" +#include "openpose/core/keyPointScaler.hpp" namespace op { - ArrayScaler::ArrayScaler(const ScaleMode scalePose) : - mScaleMode{scalePose} + KeyPointScaler::KeyPointScaler(const ScaleMode scaleMode) : + mScaleMode{scaleMode} { } - void ArrayScaler::scale(Array& array, const double scaleInputToOutput, const double scaleNetToOutput, const cv::Size& producerSize) const + void KeyPointScaler::scale(Array& arrayToScale, const double scaleInputToOutput, const double scaleNetToOutput, const cv::Size& producerSize) const { try { - std::vector> arrays{array}; - scale(arrays, scaleInputToOutput, scaleNetToOutput, producerSize); + std::vector> arrayToScalesToScale{arrayToScale}; + scale(arrayToScalesToScale, scaleInputToOutput, scaleNetToOutput, producerSize); } catch (const std::exception& e) { @@ -22,7 +22,7 @@ namespace op } } - void ArrayScaler::scale(std::vector>& arrays, const double scaleInputToOutput, const double scaleNetToOutput, const cv::Size& producerSize) const + void KeyPointScaler::scale(std::vector>& arrayToScalesToScale, const double scaleInputToOutput, const double scaleNetToOutput, const cv::Size& producerSize) const { try { @@ -30,20 +30,20 @@ namespace op { // InputResolution if (mScaleMode == ScaleMode::InputResolution) - for (auto& array : arrays) - scalePose(array, 1./scaleInputToOutput); + for (auto& arrayToScale : arrayToScalesToScale) + scaleKeyPoints(arrayToScale, 1./scaleInputToOutput); // NetOutputResolution else if (mScaleMode == ScaleMode::NetOutputResolution) - for (auto& array : arrays) - scalePose(array, 1./scaleNetToOutput); + for (auto& arrayToScale : arrayToScalesToScale) + scaleKeyPoints(arrayToScale, 1./scaleNetToOutput); // [0,1] else if (mScaleMode == ScaleMode::ZeroToOne) { const auto scale = 1./scaleInputToOutput; const auto scaleX = scale / ((double)producerSize.width - 1.); const auto scaleY = scale / ((double)producerSize.height - 1.); - for (auto& array : arrays) - scalePose(array, scaleX, scaleY); + for (auto& arrayToScale : arrayToScalesToScale) + scaleKeyPoints(arrayToScale, scaleX, scaleY); } // [-1,1] else if (mScaleMode == ScaleMode::PlusMinusOne) @@ -52,8 +52,8 @@ namespace op const auto scaleX = (scale / ((double)producerSize.width - 1.)); const auto scaleY = (scale / ((double)producerSize.height - 1.)); const auto offset = -1.; - for (auto& array : arrays) - scalePose(array, scaleX, scaleY, offset, offset); + for (auto& arrayToScale : arrayToScalesToScale) + scaleKeyPoints(arrayToScale, scaleX, scaleY, offset, offset); } // Unknown else diff --git a/src/openpose/core/resizeAndMergeBase.cu b/src/openpose/core/resizeAndMergeBase.cu index b21e51e6..231a2c43 100644 --- a/src/openpose/core/resizeAndMergeBase.cu +++ b/src/openpose/core/resizeAndMergeBase.cu @@ -82,7 +82,7 @@ namespace op } else { - if (scaleGap <= 0.f) + if (scaleGap <= 0.f && num != targetSize[0]) error("The scale gap must be greater than 0.", __LINE__, __FUNCTION__, __FILE__); const auto sourceNumOffset = channels * sourceChannelOffset; for (auto c = 0; c < channels; c++) diff --git a/src/openpose/core/scalePose.cpp b/src/openpose/core/scaleKeyPoints.cpp similarity index 63% rename from src/openpose/core/scalePose.cpp rename to src/openpose/core/scaleKeyPoints.cpp index acbdcbec..03184bbc 100644 --- a/src/openpose/core/scalePose.cpp +++ b/src/openpose/core/scaleKeyPoints.cpp @@ -1,15 +1,15 @@ #include "openpose/utilities/errorAndLog.hpp" -#include "openpose/core/scalePose.hpp" +#include "openpose/core/scaleKeyPoints.hpp" namespace op { const std::string errorMessage = "This function is only for array of dimension: [sizeA x sizeB x 3]."; - void scalePose(Array& pose, const double scale) + void scaleKeyPoints(Array& keyPoints, const double scale) { try { - scalePose(pose, scale, scale); + scaleKeyPoints(keyPoints, scale, scale); } catch (const std::exception& e) { @@ -17,18 +17,18 @@ namespace op } } - void scalePose(Array& pose, const double scaleX, const double scaleY) + void scaleKeyPoints(Array& keyPoints, const double scaleX, const double scaleY) { try { if (scaleX != 1. && scaleY != 1.) { // Error check - if (!pose.empty() && pose.getSize(2) != 3) + if (!keyPoints.empty() && keyPoints.getSize(2) != 3) error(errorMessage, __LINE__, __FUNCTION__, __FILE__); // Get #people and #parts - const auto numberPeople = pose.getSize(0); - const auto numberParts = pose.getSize(1); + const auto numberPeople = keyPoints.getSize(0); + const auto numberParts = keyPoints.getSize(1); // For each person for (auto person = 0 ; person < numberPeople ; person++) { @@ -36,8 +36,8 @@ namespace op for (auto part = 0 ; part < numberParts ; part++) { const auto finalIndex = 3*(person*numberParts + part); - pose[finalIndex] *= scaleX; - pose[finalIndex+1] *= scaleY; + keyPoints[finalIndex] *= scaleX; + keyPoints[finalIndex+1] *= scaleY; } } } @@ -48,18 +48,18 @@ namespace op } } - void scalePose(Array& pose, const double scaleX, const double scaleY, const double offsetX, const double offsetY) + void scaleKeyPoints(Array& keyPoints, const double scaleX, const double scaleY, const double offsetX, const double offsetY) { try { if (scaleX != 1. && scaleY != 1.) { // Error check - if (!pose.empty() && pose.getSize(2) != 3) + if (!keyPoints.empty() && keyPoints.getSize(2) != 3) error(errorMessage, __LINE__, __FUNCTION__, __FILE__); // Get #people and #parts - const auto numberPeople = pose.getSize(0); - const auto numberParts = pose.getSize(1); + const auto numberPeople = keyPoints.getSize(0); + const auto numberParts = keyPoints.getSize(1); // For each person for (auto person = 0 ; person < numberPeople ; person++) { @@ -67,8 +67,8 @@ namespace op for (auto part = 0 ; part < numberParts ; part++) { const auto finalIndex = 3*(person*numberParts + part); - pose[finalIndex] = pose[finalIndex] * scaleX + offsetX; - pose[finalIndex+1] = pose[finalIndex+1] * scaleY + offsetY; + keyPoints[finalIndex] = keyPoints[finalIndex] * scaleX + offsetX; + keyPoints[finalIndex+1] = keyPoints[finalIndex+1] * scaleY + offsetY; } } } diff --git a/src/openpose/experimental/face/defineTemplates.cpp b/src/openpose/experimental/face/defineTemplates.cpp new file mode 100644 index 00000000..7ef3096b --- /dev/null +++ b/src/openpose/experimental/face/defineTemplates.cpp @@ -0,0 +1,7 @@ +#include "openpose/experimental/face/headers.hpp" + +namespace op +{ + DEFINE_TEMPLATE_DATUM(experimental::WFaceExtractor); + DEFINE_TEMPLATE_DATUM(experimental::WFaceRenderer); +} diff --git a/src/openpose/experimental/face/faceExtractor.cpp b/src/openpose/experimental/face/faceExtractor.cpp new file mode 100644 index 00000000..9ab6cb07 --- /dev/null +++ b/src/openpose/experimental/face/faceExtractor.cpp @@ -0,0 +1,154 @@ +#include // CV_WARP_INVERSE_MAP, CV_INTER_LINEAR +#include "openpose/core/netCaffe.hpp" +#include "openpose/experimental/face/faceParameters.hpp" +#include "openpose/pose/poseParameters.hpp" +#include "openpose/utilities/cuda.hpp" +#include "openpose/utilities/errorAndLog.hpp" +#include "openpose/utilities/fastMath.hpp" +#include "openpose/utilities/openCv.hpp" +#include "openpose/experimental/face/faceExtractor.hpp" +#include "openpose/experimental/face/faceRenderGpu.hpp" // For commented debugging section + +namespace op +{ + namespace experimental + { + FaceExtractor::FaceExtractor(const std::string& modelFolder, const int gpuId, const PoseModel poseModel) : + mNetOutputSize{368, 368}, + mOutputSize{1280, 720}, + mNeck{poseBodyPartMapStringToKey(poseModel, "Neck")}, + mNose{poseBodyPartMapStringToKey(poseModel, std::vector{"Nose", "Head"})}, + mLEar{poseBodyPartMapStringToKey(poseModel, std::vector{"LEar", "Head"})}, + mREar{poseBodyPartMapStringToKey(poseModel, std::vector{"REar", "Head"})}, + mLEye{poseBodyPartMapStringToKey(poseModel, std::vector{"LEye", "Head"})}, + mREye{poseBodyPartMapStringToKey(poseModel, std::vector{"REye", "Head"})}, + spNet{std::make_shared(std::array{1, 3, mNetOutputSize.height, mNetOutputSize.width}, modelFolder + FACE_PROTOTXT, modelFolder + FACE_TRAINED_MODEL, gpuId)}, + spResizeAndMergeCaffe{std::make_shared>()}, + spNmsCaffe{std::make_shared>()}, + mFaceImageCrop{mNetOutputSize.area()*3} + { + try + { +error("Hands and face extraction is not implemented yet. COMING SOON!", __LINE__, __FUNCTION__, __FILE__); + // Properties + for (auto& property : mProperties) + property = 0.; + mProperties[(int)FaceProperty::NMSThreshold] = FACE_DEFAULT_NMS_THRESHOLD; + } + catch (const std::exception& e) + { + error(e.what(), __LINE__, __FUNCTION__, __FILE__); + } + } + + void FaceExtractor::initializationOnThread() + { + try + { + log("Starting initialization on thread.", Priority::Low, __LINE__, __FUNCTION__, __FILE__); + + // Get thread id + mThreadId = {std::this_thread::get_id()}; + + // Caffe net + spNet->initializationOnThread(); + spCaffeNetOutputBlob = ((NetCaffe*)spNet.get())->getOutputBlob(); + cudaCheck(__LINE__, __FUNCTION__, __FILE__); + + // HeatMaps extractor blob and layer + spHeatMapsBlob = {std::make_shared>(1,1,1,1)}; + const bool mergeFirstDimension = true; + spResizeAndMergeCaffe->Reshape({spCaffeNetOutputBlob.get()}, {spHeatMapsBlob.get()}, FACE_CCN_DECREASE_FACTOR, mergeFirstDimension); + cudaCheck(__LINE__, __FUNCTION__, __FILE__); + + // Pose extractor blob and layer + spPeaksBlob = {std::make_shared>(1,1,1,1)}; + spNmsCaffe->Reshape({spHeatMapsBlob.get()}, {spPeaksBlob.get()}, FACE_MAX_PEAKS, FACE_NUMBER_PARTS); + cudaCheck(__LINE__, __FUNCTION__, __FILE__); + + log("Finished initialization on thread.", Priority::Low, __LINE__, __FUNCTION__, __FILE__); + } + catch (const std::exception& e) + { + error(e.what(), __LINE__, __FUNCTION__, __FILE__); + } + } + + void FaceExtractor::forwardPass(const Array& poseKeyPoints, const cv::Mat& cvInputData) + { + try + { +UNUSED(poseKeyPoints); +UNUSED(cvInputData); + } + catch (const std::exception& e) + { + error(e.what(), __LINE__, __FUNCTION__, __FILE__); + } + } + + Array FaceExtractor::getFaceKeyPoints() const + { + try + { + checkThread(); + return mFaceKeyPoints; + } + catch (const std::exception& e) + { + error(e.what(), __LINE__, __FUNCTION__, __FILE__); + return Array{}; + } + } + + double FaceExtractor::get(const FaceProperty property) const + { + try + { + return mProperties.at((int)property); + } + catch (const std::exception& e) + { + error(e.what(), __LINE__, __FUNCTION__, __FILE__); + return 0.; + } + } + + void FaceExtractor::set(const FaceProperty property, const double value) + { + try + { + mProperties.at((int)property) = {value}; + } + catch (const std::exception& e) + { + error(e.what(), __LINE__, __FUNCTION__, __FILE__); + } + } + + void FaceExtractor::increase(const FaceProperty property, const double value) + { + try + { + mProperties[(int)property] = mProperties.at((int)property) + value; + } + catch (const std::exception& e) + { + error(e.what(), __LINE__, __FUNCTION__, __FILE__); + } + } + + void FaceExtractor::checkThread() const + { + try + { + if(mThreadId != std::this_thread::get_id()) + error("The CPU/GPU pointer data cannot be accessed from a different thread.", __LINE__, __FUNCTION__, __FILE__); + } + catch (const std::exception& e) + { + error(e.what(), __LINE__, __FUNCTION__, __FILE__); + } + } + } +} diff --git a/src/openpose/experimental/face/faceRenderGpu.cu b/src/openpose/experimental/face/faceRenderGpu.cu new file mode 100644 index 00000000..9811ed12 --- /dev/null +++ b/src/openpose/experimental/face/faceRenderGpu.cu @@ -0,0 +1,65 @@ +#include "openpose/experimental/face/faceParameters.hpp" +#include "openpose/utilities/errorAndLog.hpp" +#include "openpose/utilities/cuda.hpp" +#include "openpose/utilities/cuda.hu" +#include "openpose/utilities/render.hu" +#include "openpose/experimental/face/faceRenderGpu.hpp" + +namespace op +{ + const auto THREADS_PER_BLOCK_1D = 32; + + __constant__ const unsigned char PART_PAIRS_GPU[] = FACE_PAIRS_TO_RENDER; + __constant__ const float RGB_COLORS[] = { + 255.f, 255.f, 255.f, + }; + + + + __global__ void renderFaceParts(float* targetPtr, const int targetWidth, const int targetHeight, const float* const facePtr, + const int numberFaces, const float threshold, const float alphaColorToAdd) + { + const auto x = (blockIdx.x * blockDim.x) + threadIdx.x; + const auto y = (blockIdx.y * blockDim.y) + threadIdx.y; + const auto globalIdx = threadIdx.y * blockDim.x + threadIdx.x; + + // Shared parameters + __shared__ float2 sharedMins[FACE_MAX_NUMBER_FACE]; + __shared__ float2 sharedMaxs[FACE_MAX_NUMBER_FACE]; + __shared__ float sharedScaleF[FACE_MAX_NUMBER_FACE]; + + // Other parameters + const auto numberPartPairs = sizeof(PART_PAIRS_GPU) / (2*sizeof(PART_PAIRS_GPU[0])); + const auto numberColors = sizeof(RGB_COLORS) / (3*sizeof(RGB_COLORS[0])); + const auto radius = fastMin(targetWidth, targetHeight) / 120.f; + const auto stickwidth = fastMin(targetWidth, targetHeight) / 250.f; + + // Render key points + renderKeyPoints(targetPtr, sharedMaxs, sharedMins, sharedScaleF, + globalIdx, x, y, targetWidth, targetHeight, facePtr, PART_PAIRS_GPU, numberFaces, + FACE_NUMBER_PARTS, numberPartPairs, RGB_COLORS, numberColors, + radius, stickwidth, threshold, alphaColorToAdd); + } + + void renderFaceGpu(float* framePtr, const cv::Size& frameSize, const float* const facePtr, const int numberFaces, const float alphaColorToAdd) + { + try + { + if (numberFaces > 0) + { + //framePtr = width * height * 3 + //facePtr = 3 (x,y,score) * #Hand parts * # face + const auto threshold = 0.4f; + dim3 threadsPerBlock = dim3{THREADS_PER_BLOCK_1D, THREADS_PER_BLOCK_1D}; + dim3 numBlocks = dim3{getNumberCudaBlocks(frameSize.width, threadsPerBlock.x), getNumberCudaBlocks(frameSize.height, threadsPerBlock.y)}; + // log("numberFaces: " + std::to_string(numberFaces), Priority::Low, __LINE__, __FUNCTION__, __FILE__); + renderFaceParts<<>>(framePtr, frameSize.width, frameSize.height, facePtr, numberFaces, threshold, alphaColorToAdd); + cudaCheck(__LINE__, __FUNCTION__, __FILE__); + } + } + catch (const std::exception& e) + { + error(e.what(), __LINE__, __FUNCTION__, __FILE__); + } + } +} diff --git a/src/openpose/experimental/face/faceRenderer.cpp b/src/openpose/experimental/face/faceRenderer.cpp new file mode 100644 index 00000000..71022038 --- /dev/null +++ b/src/openpose/experimental/face/faceRenderer.cpp @@ -0,0 +1,66 @@ +#include +#include +#include "openpose/experimental/face/faceParameters.hpp" +#include "openpose/experimental/face/faceRenderGpu.hpp" +#include "openpose/utilities/cuda.hpp" +#include "openpose/utilities/errorAndLog.hpp" +#include "openpose/experimental/face/faceRenderer.hpp" + +namespace op +{ + namespace experimental + { + FaceRenderer::FaceRenderer(const cv::Size& frameSize) : + Renderer{(unsigned long long)(frameSize.area() * 3)}, + mFrameSize{frameSize} + { + } + + FaceRenderer::~FaceRenderer() + { + try + { + // Free CUDA pointers - Note that if pointers are 0 (i.e. nullptr), no operation is performed. + cudaFree(pGpuFace); + } + catch (const std::exception& e) + { + error(e.what(), __LINE__, __FUNCTION__, __FILE__); + } + } + + void FaceRenderer::initializationOnThread() + { + try + { + Renderer::initializationOnThread(); + cudaMalloc((void**)(&pGpuFace), FACE_NUMBER_PARTS * 3 * sizeof(float) ); + } + catch (const std::exception& e) + { + error(e.what(), __LINE__, __FUNCTION__, __FILE__); + } + } + + void FaceRenderer::renderFace(Array& outputData, const Array& faceKeyPoints) + { + try + { + if (!faceKeyPoints.empty()) + { + cpuToGpuMemoryIfNotCopiedYet(outputData.getPtr()); + cudaMemcpy(pGpuFace, faceKeyPoints.getConstPtr(), FACE_NUMBER_PARTS*3 * sizeof(float), cudaMemcpyHostToDevice); + renderFaceGpu(*spGpuMemoryPtr, mFrameSize, pGpuFace, faceKeyPoints.getSize(0)); + cudaCheck(__LINE__, __FUNCTION__, __FILE__); + } + // GPU memory to CPU if last renderer + gpuToCpuMemoryIfLastRenderer(outputData.getPtr()); + cudaCheck(__LINE__, __FUNCTION__, __FILE__); + } + catch (const std::exception& e) + { + error(e.what(), __LINE__, __FUNCTION__, __FILE__); + } + } + } +} diff --git a/src/openpose/experimental/hand/defineTemplates.cpp b/src/openpose/experimental/hand/defineTemplates.cpp new file mode 100644 index 00000000..23e155b7 --- /dev/null +++ b/src/openpose/experimental/hand/defineTemplates.cpp @@ -0,0 +1,7 @@ +#include "openpose/experimental/hand/headers.hpp" + +namespace op +{ + DEFINE_TEMPLATE_DATUM(experimental::WHandExtractor); + DEFINE_TEMPLATE_DATUM(experimental::WHandRenderer); +} diff --git a/src/openpose/experimental/hand/handExtractor.cpp b/src/openpose/experimental/hand/handExtractor.cpp new file mode 100644 index 00000000..ecd4128d --- /dev/null +++ b/src/openpose/experimental/hand/handExtractor.cpp @@ -0,0 +1,157 @@ +#include // CV_WARP_INVERSE_MAP, CV_INTER_LINEAR +#include "openpose/core/netCaffe.hpp" +#include "openpose/experimental/hand/handParameters.hpp" +#include "openpose/pose/poseParameters.hpp" +#include "openpose/utilities/cuda.hpp" +#include "openpose/utilities/errorAndLog.hpp" +#include "openpose/utilities/fastMath.hpp" +#include "openpose/utilities/openCv.hpp" +#include "openpose/experimental/hand/handExtractor.hpp" +// #include "openpose/experimental/hand/handRenderGpu.hpp" // For commented debugging section + +namespace op +{ + namespace experimental + { + HandExtractor::HandExtractor(const std::string& modelFolder, const int gpuId, const PoseModel poseModel) : + mNetOutputSize{368, 368}, + mOutputSize{1280, 720}, + mRWrist{poseBodyPartMapStringToKey(poseModel, "RWrist")}, + mRElbow{poseBodyPartMapStringToKey(poseModel, "RElbow")}, + mLWrist{poseBodyPartMapStringToKey(poseModel, "LWrist")}, + mLElbow{poseBodyPartMapStringToKey(poseModel, "LElbow")}, + mNeck{poseBodyPartMapStringToKey(poseModel, "Neck")}, + mHeadNose{poseBodyPartMapStringToKey(poseModel, std::vector{"Nose", "Head"})}, + spNet{std::make_shared(std::array{2, 3, mNetOutputSize.height, mNetOutputSize.width}, modelFolder + HAND_PROTOTXT, modelFolder + HAND_TRAINED_MODEL, gpuId)}, + spResizeAndMergeCaffe{std::make_shared>()}, + spNmsCaffe{std::make_shared>()}, + mLeftHandCrop{mNetOutputSize.area()*3}, + mRightHandCrop{mLeftHandCrop.getSize()}, + mScaleLeftHand{100.f}, + mScaleRightHand{mScaleLeftHand} + { + try + { +error("Hands and face extraction is not implemented yet. COMING SOON!", __LINE__, __FUNCTION__, __FILE__); + // Properties + for (auto& property : mProperties) + property = 0.; + mProperties[(int)HandsProperty::NMSThreshold] = HAND_DEFAULT_NMS_THRESHOLD; + } + catch (const std::exception& e) + { + error(e.what(), __LINE__, __FUNCTION__, __FILE__); + } + } + + void HandExtractor::initializationOnThread() + { + try + { + log("Starting initialization on thread.", Priority::Low, __LINE__, __FUNCTION__, __FILE__); + + // Get thread id + mThreadId = {std::this_thread::get_id()}; + + // Caffe net + spNet->initializationOnThread(); + spCaffeNetOutputBlob = ((NetCaffe*)spNet.get())->getOutputBlob(); + cudaCheck(__LINE__, __FUNCTION__, __FILE__); + + // HeatMaps extractor blob and layer + spHeatMapsBlob = {std::make_shared>(1,1,1,1)}; + const bool mergeFirstDimension = false; + spResizeAndMergeCaffe->Reshape({spCaffeNetOutputBlob.get()}, {spHeatMapsBlob.get()}, HAND_CCN_DECREASE_FACTOR, mergeFirstDimension); + cudaCheck(__LINE__, __FUNCTION__, __FILE__); + + // Pose extractor blob and layer + spPeaksBlob = {std::make_shared>(1,1,1,1)}; + spNmsCaffe->Reshape({spHeatMapsBlob.get()}, {spPeaksBlob.get()}, HAND_MAX_PEAKS, HAND_NUMBER_PARTS); + cudaCheck(__LINE__, __FUNCTION__, __FILE__); + + log("Finished initialization on thread.", Priority::Low, __LINE__, __FUNCTION__, __FILE__); + } + catch (const std::exception& e) + { + error(e.what(), __LINE__, __FUNCTION__, __FILE__); + } + } + + void HandExtractor::forwardPass(const Array& poseKeyPoints, const cv::Mat& cvInputData) + { + try + { +UNUSED(poseKeyPoints); +UNUSED(cvInputData); + } + catch (const std::exception& e) + { + error(e.what(), __LINE__, __FUNCTION__, __FILE__); + } + } + + Array HandExtractor::getHandKeyPoints() const + { + try + { + checkThread(); + return mHands; + } + catch (const std::exception& e) + { + error(e.what(), __LINE__, __FUNCTION__, __FILE__); + return Array{}; + } + } + + double HandExtractor::get(const HandsProperty property) const + { + try + { + return mProperties.at((int)property); + } + catch (const std::exception& e) + { + error(e.what(), __LINE__, __FUNCTION__, __FILE__); + return 0.; + } + } + + void HandExtractor::set(const HandsProperty property, const double value) + { + try + { + mProperties.at((int)property) = {value}; + } + catch (const std::exception& e) + { + error(e.what(), __LINE__, __FUNCTION__, __FILE__); + } + } + + void HandExtractor::increase(const HandsProperty property, const double value) + { + try + { + mProperties[(int)property] = mProperties.at((int)property) + value; + } + catch (const std::exception& e) + { + error(e.what(), __LINE__, __FUNCTION__, __FILE__); + } + } + + void HandExtractor::checkThread() const + { + try + { + if(mThreadId != std::this_thread::get_id()) + error("The CPU/GPU pointer data cannot be accessed from a different thread.", __LINE__, __FUNCTION__, __FILE__); + } + catch (const std::exception& e) + { + error(e.what(), __LINE__, __FUNCTION__, __FILE__); + } + } + } +} diff --git a/src/openpose/experimental/hand/handRenderGpu.cu b/src/openpose/experimental/hand/handRenderGpu.cu new file mode 100644 index 00000000..8c53d6e1 --- /dev/null +++ b/src/openpose/experimental/hand/handRenderGpu.cu @@ -0,0 +1,88 @@ +#include "openpose/experimental/hand/handParameters.hpp" +#include "openpose/utilities/errorAndLog.hpp" +#include "openpose/utilities/cuda.hpp" +#include "openpose/utilities/cuda.hu" +#include "openpose/utilities/render.hu" +#include "openpose/experimental/hand/handRenderGpu.hpp" + +namespace op +{ + const auto THREADS_PER_BLOCK_1D = 32; + + __constant__ const unsigned char PART_PAIRS_GPU[] = HAND_PAIRS_TO_RENDER; + __constant__ const float RGB_COLORS[] = { + 179.f, 0.f, 0.f, + 204.f, 0.f, 0.f, + 230.f, 0.f, 0.f, + 255.f, 0.f, 0.f, + 143.f, 179.f, 0.f, + 163.f, 204.f, 0.f, + 184.f, 230.f, 0.f, + 204.f, 255.f, 0.f, + 0.f, 179.f, 71.f, + 0.f, 204.f, 82.f, + 0.f, 230.f, 92.f, + 0.f, 255.f, 102.f, + 0.f, 71.f, 179.f, + 0.f, 82.f, 204.f, + 0.f, 92.f, 230.f, + 0.f, 102.f, 255.f, + 143.f, 0.f, 179.f, + 163.f, 0.f, 204.f, + 184.f, 0.f, 230.f, + 204.f, 0.f, 255.f, + 179.f, 179.f, 179.f, + 179.f, 179.f, 179.f, + 179.f, 179.f, 179.f, + 179.f, 179.f, 179.f + }; + + + + __global__ void renderHandsParts(float* targetPtr, const int targetWidth, const int targetHeight, const float* const handsPtr, + const int numberHands, const float threshold, const float alphaColorToAdd) + { + const auto x = (blockIdx.x * blockDim.x) + threadIdx.x; + const auto y = (blockIdx.y * blockDim.y) + threadIdx.y; + const auto globalIdx = threadIdx.y * blockDim.x + threadIdx.x; + + // Shared parameters + __shared__ float2 sharedMins[HAND_MAX_NUMBER_HANDS]; + __shared__ float2 sharedMaxs[HAND_MAX_NUMBER_HANDS]; + __shared__ float sharedScaleF[HAND_MAX_NUMBER_HANDS]; + + // Other parameters + const auto numberPartPairs = sizeof(PART_PAIRS_GPU) / (2*sizeof(PART_PAIRS_GPU[0])); + const auto numberColors = sizeof(RGB_COLORS) / (3*sizeof(RGB_COLORS[0])); + const auto radius = fastMin(targetWidth, targetHeight) / 100.f; + const auto stickwidth = fastMin(targetWidth, targetHeight) / 80.f; + + // Render key points + renderKeyPoints(targetPtr, sharedMaxs, sharedMins, sharedScaleF, + globalIdx, x, y, targetWidth, targetHeight, handsPtr, PART_PAIRS_GPU, numberHands, + HAND_NUMBER_PARTS, numberPartPairs, RGB_COLORS, numberColors, + radius, stickwidth, threshold, alphaColorToAdd); + } + + void renderHandsGpu(float* framePtr, const cv::Size& frameSize, const float* const handsPtr, const int numberHands, const float alphaColorToAdd) + { + try + { + if (numberHands > 0) + { + //framePtr = width * height * 3 + //handsPtr = 3 (x,y,score) * #Hand parts * # hands + const auto threshold = 0.05f; + dim3 threadsPerBlock = dim3{THREADS_PER_BLOCK_1D, THREADS_PER_BLOCK_1D}; + dim3 numBlocks = dim3{getNumberCudaBlocks(frameSize.width, threadsPerBlock.x), getNumberCudaBlocks(frameSize.height, threadsPerBlock.y)}; + // log("numberHands: " + std::to_string(numberHands), Priority::Low, __LINE__, __FUNCTION__, __FILE__); + renderHandsParts<<>>(framePtr, frameSize.width, frameSize.height, handsPtr, numberHands, threshold, alphaColorToAdd); + cudaCheck(__LINE__, __FUNCTION__, __FILE__); + } + } + catch (const std::exception& e) + { + error(e.what(), __LINE__, __FUNCTION__, __FILE__); + } + } +} diff --git a/src/openpose/experimental/hand/handRenderer.cpp b/src/openpose/experimental/hand/handRenderer.cpp new file mode 100644 index 00000000..885375fd --- /dev/null +++ b/src/openpose/experimental/hand/handRenderer.cpp @@ -0,0 +1,66 @@ +#include +#include +#include "openpose/experimental/hand/handParameters.hpp" +#include "openpose/experimental/hand/handRenderGpu.hpp" +#include "openpose/utilities/cuda.hpp" +#include "openpose/utilities/errorAndLog.hpp" +#include "openpose/experimental/hand/handRenderer.hpp" + +namespace op +{ + namespace experimental + { + HandRenderer::HandRenderer(const cv::Size& frameSize) : + Renderer{(unsigned long long)(frameSize.area() * 3)}, + mFrameSize{frameSize} + { + } + + HandRenderer::~HandRenderer() + { + try + { + // Free CUDA pointers - Note that if pointers are 0 (i.e. nullptr), no operation is performed. + cudaFree(pGpuHands); + } + catch (const std::exception& e) + { + error(e.what(), __LINE__, __FUNCTION__, __FILE__); + } + } + + void HandRenderer::initializationOnThread() + { + try + { + Renderer::initializationOnThread(); + cudaMalloc((void**)(&pGpuHands), 2*HAND_NUMBER_PARTS * 3 * sizeof(float) ); + } + catch (const std::exception& e) + { + error(e.what(), __LINE__, __FUNCTION__, __FILE__); + } + } + + void HandRenderer::renderHands(Array& outputData, const Array& handKeyPoints) + { + try + { + if (!handKeyPoints.empty()) + { + cpuToGpuMemoryIfNotCopiedYet(outputData.getPtr()); + cudaMemcpy(pGpuHands, handKeyPoints.getConstPtr(), 2*HAND_NUMBER_PARTS*3 * sizeof(float), cudaMemcpyHostToDevice); + renderHandsGpu(*spGpuMemoryPtr, mFrameSize, pGpuHands, handKeyPoints.getSize(0)); + cudaCheck(__LINE__, __FUNCTION__, __FILE__); + } + // GPU memory to CPU if last renderer + gpuToCpuMemoryIfLastRenderer(outputData.getPtr()); + cudaCheck(__LINE__, __FUNCTION__, __FILE__); + } + catch (const std::exception& e) + { + error(e.what(), __LINE__, __FUNCTION__, __FILE__); + } + } + } +} diff --git a/src/openpose/experimental/hands/defineTemplates.cpp b/src/openpose/experimental/hands/defineTemplates.cpp deleted file mode 100644 index 48dbf213..00000000 --- a/src/openpose/experimental/hands/defineTemplates.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "openpose/experimental/hands/headers.hpp" - -namespace op -{ - DEFINE_TEMPLATE_DATUM(experimental::WHandsExtractor); - DEFINE_TEMPLATE_DATUM(experimental::WHandsRenderer); -} diff --git a/src/openpose/experimental/hands/handsExtractor.cpp b/src/openpose/experimental/hands/handsExtractor.cpp deleted file mode 100644 index ca614ae0..00000000 --- a/src/openpose/experimental/hands/handsExtractor.cpp +++ /dev/null @@ -1,117 +0,0 @@ -#include "openpose/utilities/errorAndLog.hpp" -#include "openpose/experimental/hands/handsExtractor.hpp" - -namespace op -{ - namespace experimental - { - HandsExtractor::HandsExtractor(const std::string& modelFolder, const int gpuId, const PoseModel poseModel) : - mNetOutputSize{368, 368}, - mOutputSize{1280, 720}, - mRWrist{0}, - mRElbow{0}, - mLWrist{0}, - mLElbow{0}, - mNeck{0}, - mHeadNose{0}, - spNet{}, - spResizeAndMergeCaffe{}, - spNmsCaffe{}, - mLeftHandCrop{mNetOutputSize.area()*3}, - mRightHandCrop{mLeftHandCrop.getSize()}, - mScaleLeftHand{0.f}, - mScaleRightHand{0.f} - { - UNUSED(modelFolder); - UNUSED(gpuId); - UNUSED(poseModel); - error("Hands code is not ready yet. A first beta version will be included in around 1-2 months. Please, set extractAndRenderHands = false in the OpenPose wrapper.", - __LINE__, __FUNCTION__, __FILE__); - } - - void HandsExtractor::initializationOnThread() - { - error("Hands code is not ready yet. A first beta version will be included in around 1-2 months. Please, set extractAndRenderHands = false in the OpenPose wrapper.", - __LINE__, __FUNCTION__, __FILE__); - } - - void HandsExtractor::forwardPass(const Array& pose, const cv::Mat& cvInputData) - { - try - { - UNUSED(pose); - UNUSED(cvInputData); - error("Hands code is not ready yet. A first beta version will be included in around 1-2 months. Please, set extractAndRenderHands = false in the OpenPose wrapper.", - __LINE__, __FUNCTION__, __FILE__); - } - catch (const std::exception& e) - { - error(e.what(), __LINE__, __FUNCTION__, __FILE__); - } - } - - Array HandsExtractor::getHands() const - { - try - { - checkThread(); - return mHands; - } - catch (const std::exception& e) - { - error(e.what(), __LINE__, __FUNCTION__, __FILE__); - return Array{}; - } - } - - double HandsExtractor::get(const HandsProperty property) const - { - try - { - return mProperties.at((int)property); - } - catch (const std::exception& e) - { - error(e.what(), __LINE__, __FUNCTION__, __FILE__); - return 0.; - } - } - - void HandsExtractor::set(const HandsProperty property, const double value) - { - try - { - mProperties.at((int)property) = {value}; - } - catch (const std::exception& e) - { - error(e.what(), __LINE__, __FUNCTION__, __FILE__); - } - } - - void HandsExtractor::increase(const HandsProperty property, const double value) - { - try - { - mProperties[(int)property] = mProperties.at((int)property) + value; - } - catch (const std::exception& e) - { - error(e.what(), __LINE__, __FUNCTION__, __FILE__); - } - } - - void HandsExtractor::checkThread() const - { - try - { - if(mThreadId != std::this_thread::get_id()) - error("The CPU/GPU pointer data cannot be accessed from a different thread.", __LINE__, __FUNCTION__, __FILE__); - } - catch (const std::exception& e) - { - error(e.what(), __LINE__, __FUNCTION__, __FILE__); - } - } - } -} diff --git a/src/openpose/experimental/hands/handsRenderGpu.cu b/src/openpose/experimental/hands/handsRenderGpu.cu deleted file mode 100644 index c5e5438e..00000000 --- a/src/openpose/experimental/hands/handsRenderGpu.cu +++ /dev/null @@ -1,18 +0,0 @@ -#include "openpose/utilities/errorAndLog.hpp" -#include "openpose/experimental/hands/handsRenderGpu.hpp" - -namespace op -{ - void renderHandsGpu(float* framePtr, const cv::Size& frameSize, const float* const handsPtr, const int numberHands, const float alphaColorToAdd) - { - try - { - error("Hands code is not ready yet. A first beta version will be included in around 1-2 months. Please, set extractAndRenderHands = false in the OpenPose wrapper.", - __LINE__, __FUNCTION__, __FILE__); - } - catch (const std::exception& e) - { - error(e.what(), __LINE__, __FUNCTION__, __FILE__); - } - } -} diff --git a/src/openpose/experimental/hands/handsRenderer.cpp b/src/openpose/experimental/hands/handsRenderer.cpp deleted file mode 100644 index cb09a77b..00000000 --- a/src/openpose/experimental/hands/handsRenderer.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include "openpose/utilities/errorAndLog.hpp" -#include "openpose/experimental/hands/handsRenderer.hpp" - -namespace op -{ - namespace experimental - { - HandsRenderer::HandsRenderer(const cv::Size& frameSize) : - Renderer{(unsigned long long)(frameSize.area() * 3)}, - mFrameSize{frameSize} - { - error("Hands code is not ready yet. A first beta version will be included in around 1-2 months. Please, set extractAndRenderHands = false in the OpenPose wrapper.", - __LINE__, __FUNCTION__, __FILE__); - } - - HandsRenderer::~HandsRenderer() - { - } - - void HandsRenderer::initializationOnThread() - { - try - { - error("Hands code is not ready yet. A first beta version will be included in around 1-2 months. Please, set extractAndRenderHands = false in the OpenPose wrapper.", - __LINE__, __FUNCTION__, __FILE__); - } - catch (const std::exception& e) - { - error(e.what(), __LINE__, __FUNCTION__, __FILE__); - } - } - - void HandsRenderer::renderHands(Array& outputData, const Array& hands) - { - try - { - UNUSED(outputData); - UNUSED(hands); - error("Hands code is not ready yet. A first beta version will be included in around 1-2 months. Please, set extractAndRenderHands = false in the OpenPose wrapper.", - __LINE__, __FUNCTION__, __FILE__); - } - catch (const std::exception& e) - { - error(e.what(), __LINE__, __FUNCTION__, __FILE__); - } - } - } -} diff --git a/src/openpose/filestream/poseJsonCocoSaver.cpp b/src/openpose/filestream/poseJsonCocoSaver.cpp index 5a5be676..14290547 100644 --- a/src/openpose/filestream/poseJsonCocoSaver.cpp +++ b/src/openpose/filestream/poseJsonCocoSaver.cpp @@ -28,12 +28,12 @@ namespace op } } - void PoseJsonCocoSaver::record(const Array& pose, const int imageId) + void PoseJsonCocoSaver::record(const Array& poseKeyPoints, const int imageId) { try { - const auto numberPeople = pose.getSize(0); - const auto numberBodyParts = pose.getSize(1); + const auto numberPeople = poseKeyPoints.getSize(0); + const auto numberBodyParts = poseKeyPoints.getSize(1); for (auto person = 0 ; person < numberPeople ; person++) { // Comma at any moment but first element @@ -58,16 +58,16 @@ namespace op mJsonOfstream.plainText("1"); mJsonOfstream.comma(); - // keypoints - i.e. pose + // keypoints - i.e. poseKeyPoints mJsonOfstream.key("keypoints"); mJsonOfstream.arrayOpen(); const std::vector indexesInCocoOrder{0, 15, 14, 17, 16, 5, 2, 6, 3, 7, 4, 11, 8, 12, 9, 13, 10}; for (auto bodyPart = 0 ; bodyPart < indexesInCocoOrder.size() ; bodyPart++) { const auto finalIndex = 3*(person*numberBodyParts + indexesInCocoOrder.at(bodyPart)); - mJsonOfstream.plainText(pose[finalIndex]); + mJsonOfstream.plainText(poseKeyPoints[finalIndex]); mJsonOfstream.comma(); - mJsonOfstream.plainText(pose[finalIndex+1]); + mJsonOfstream.plainText(poseKeyPoints[finalIndex+1]); mJsonOfstream.comma(); mJsonOfstream.plainText(1); if (bodyPart < numberBodyParts-1) diff --git a/src/openpose/filestream/poseJsonSaver.cpp b/src/openpose/filestream/poseJsonSaver.cpp index 52333b61..20f022a3 100644 --- a/src/openpose/filestream/poseJsonSaver.cpp +++ b/src/openpose/filestream/poseJsonSaver.cpp @@ -9,21 +9,21 @@ namespace op { } - void PoseJsonSaver::savePoseVector(const std::vector>& poseVector, const std::string& fileName) const + void PoseJsonSaver::savePoseKeyPoints(const std::vector>& poseKeyPointsVector, const std::string& fileName) const { try { // Record json - if (!poseVector.empty()) + if (!poseKeyPointsVector.empty()) { // File path (no extension) const auto fileNameNoExtension = getNextFileName(fileName) + "_pose"; const bool humanReadable = true; - for (auto i = 0; i < poseVector.size(); i++) + for (auto i = 0; i < poseKeyPointsVector.size(); i++) { const auto fileName = fileNameNoExtension + (i != 0 ? "_" + std::to_string(i) : "") + ".json"; - savePoseJson(poseVector[i], fileName, humanReadable); + savePoseJson(poseKeyPointsVector[i], fileName, humanReadable); } } } diff --git a/src/openpose/filestream/poseSaver.cpp b/src/openpose/filestream/poseSaver.cpp index a8cb103c..de8c959b 100644 --- a/src/openpose/filestream/poseSaver.cpp +++ b/src/openpose/filestream/poseSaver.cpp @@ -11,27 +11,27 @@ namespace op { } - void PoseSaver::savePoseVector(const std::vector>& poseVector, const std::string& fileName) const + void PoseSaver::savePoseKeyPoints(const std::vector>& poseKeyPointsVector, const std::string& fileName) const { try { - if (!poseVector.empty()) + if (!poseKeyPointsVector.empty()) { // File path (no extension) const auto fileNameNoExtension = getNextFileName(fileName) + "_pose"; // Get vector of people poses - std::vector cvMatPoses(poseVector.size()); - for (auto i = 0; i < poseVector.size(); i++) - cvMatPoses[i] = poseVector[i].getConstCvMat(); + std::vector cvMatPoses(poseKeyPointsVector.size()); + for (auto i = 0; i < poseKeyPointsVector.size(); i++) + cvMatPoses[i] = poseKeyPointsVector[i].getConstCvMat(); // Get names inside file - std::vector poseVectorNames(cvMatPoses.size()); + std::vector poseKeyPointsVectorNames(cvMatPoses.size()); for (auto i = 0; i < cvMatPoses.size(); i++) - poseVectorNames[i] = {"pose_" + std::to_string(i)}; + poseKeyPointsVectorNames[i] = {"pose_" + std::to_string(i)}; // Record people poses in desired format - saveData(cvMatPoses, poseVectorNames, fileNameNoExtension, mFormat); + saveData(cvMatPoses, poseKeyPointsVectorNames, fileNameNoExtension, mFormat); } } catch (const std::exception& e) diff --git a/src/openpose/gui/guiInfoAdder.cpp b/src/openpose/gui/guiInfoAdder.cpp index 786026cf..2e8ee3cb 100644 --- a/src/openpose/gui/guiInfoAdder.cpp +++ b/src/openpose/gui/guiInfoAdder.cpp @@ -49,7 +49,7 @@ namespace op { } - void GuiInfoAdder::addInfo(cv::Mat& cvOutputData, const Array& pose, const unsigned long long id, const std::string& elementRenderedName) + void GuiInfoAdder::addInfo(cv::Mat& cvOutputData, const Array& poseKeyPoints, const unsigned long long id, const std::string& elementRenderedName) { try { @@ -83,7 +83,7 @@ namespace op // Frame number putTextOnCvMat(cvOutputData, "Frame " + std::to_string(id), {mBorderMargin, (int)(mOutputSize.height - mBorderMargin)}, white, false); // Number people - const auto textToDisplay = std::to_string(pose.getSize(0)) + " people"; + const auto textToDisplay = std::to_string(poseKeyPoints.getSize(0)) + " people"; putTextOnCvMat(cvOutputData, textToDisplay, {(int)(mOutputSize.width - mBorderMargin), (int)(mOutputSize.height - mBorderMargin)}, white, true); } catch (const std::exception& e) diff --git a/src/openpose/pose/bodyPartConnectorBase.cpp b/src/openpose/pose/bodyPartConnectorBase.cpp index 225b9ddc..968ea682 100644 --- a/src/openpose/pose/bodyPartConnectorBase.cpp +++ b/src/openpose/pose/bodyPartConnectorBase.cpp @@ -7,7 +7,7 @@ namespace op { template - void connectBodyPartsCpu(Array& pose, const T* const heatMapPtr, const T* const peaksPtr, const PoseModel poseModel, const cv::Size& heatMapSize, const int maxPeaks, + void connectBodyPartsCpu(Array& poseKeyPoints, const T* const heatMapPtr, const T* const peaksPtr, const PoseModel poseModel, const cv::Size& heatMapSize, const int maxPeaks, const int interMinAboveThreshold, const T interThreshold, const int minSubsetCnt, const T minSubsetScore, const T scaleFactor) { try @@ -290,11 +290,11 @@ namespace op error("Bad subsetCounter. Bug in in this function if this happens.", __LINE__, __FUNCTION__, __FILE__); } - // Fill and return pose + // Fill and return poseKeyPoints if (numberPeople > 0) - pose.reset({numberPeople, numberBodyParts, 3}); + poseKeyPoints.reset({numberPeople, numberBodyParts, 3}); else - pose.reset(); + poseKeyPoints.reset(); for (auto person = 0 ; person < validSubsetIndexes.size() ; person++) { const auto& subsetI = subset[validSubsetIndexes[person]].first; @@ -304,15 +304,15 @@ namespace op const auto bodyPartIndex = subsetI[bodyPart]; if (bodyPartIndex > 0) { - pose[baseOffset] = peaksPtr[bodyPartIndex-2] * scaleFactor; - pose[baseOffset + 1] = peaksPtr[bodyPartIndex-1] * scaleFactor; - pose[baseOffset + 2] = peaksPtr[bodyPartIndex]; + poseKeyPoints[baseOffset] = peaksPtr[bodyPartIndex-2] * scaleFactor; + poseKeyPoints[baseOffset + 1] = peaksPtr[bodyPartIndex-1] * scaleFactor; + poseKeyPoints[baseOffset + 2] = peaksPtr[bodyPartIndex]; } else { - pose[baseOffset] = 0.f; - pose[baseOffset + 1] = 0.f; - pose[baseOffset + 2] = 0.f; + poseKeyPoints[baseOffset] = 0.f; + poseKeyPoints[baseOffset + 1] = 0.f; + poseKeyPoints[baseOffset + 2] = 0.f; } } } @@ -323,10 +323,10 @@ namespace op } } - template void connectBodyPartsCpu(Array& pose, const float* const heatMapPtr, const float* const peaksPtr, const PoseModel poseModel, const cv::Size& heatMapSize, + template void connectBodyPartsCpu(Array& poseKeyPoints, const float* const heatMapPtr, const float* const peaksPtr, const PoseModel poseModel, const cv::Size& heatMapSize, const int maxPeaks, const int interMinAboveThreshold, const float interThreshold, const int minSubsetCnt, const float minSubsetScore, const float scaleFactor); - template void connectBodyPartsCpu(Array& pose, const double* const heatMapPtr, const double* const peaksPtr, const PoseModel poseModel, const cv::Size& heatMapSize, + template void connectBodyPartsCpu(Array& poseKeyPoints, const double* const heatMapPtr, const double* const peaksPtr, const PoseModel poseModel, const cv::Size& heatMapSize, const int maxPeaks, const int interMinAboveThreshold, const double interThreshold, const int minSubsetCnt, const double minSubsetScore, const double scaleFactor); } diff --git a/src/openpose/pose/bodyPartConnectorBase.cu b/src/openpose/pose/bodyPartConnectorBase.cu index d922f0a2..d3610693 100644 --- a/src/openpose/pose/bodyPartConnectorBase.cu +++ b/src/openpose/pose/bodyPartConnectorBase.cu @@ -5,12 +5,12 @@ namespace op { template - void connectBodyPartsGpu(Array& pose, T* posePtr, const T* const heatMapPtr, const T* const peaksPtr, const PoseModel poseModel, const cv::Size& heatMapSize, + void connectBodyPartsGpu(Array& poseKeyPoints, T* posePtr, const T* const heatMapPtr, const T* const peaksPtr, const PoseModel poseModel, const cv::Size& heatMapSize, const int maxPeaks, const int interMinAboveThreshold, const T interThreshold, const int minSubsetCnt, const T minSubsetScore, const T scaleFactor) { try { - UNUSED(pose); + UNUSED(poseKeyPoints); UNUSED(posePtr); UNUSED(heatMapPtr); UNUSED(peaksPtr); @@ -31,10 +31,10 @@ namespace op } } - template void connectBodyPartsGpu(Array& pose, float* posePtr, const float* const heatMapPtr, const float* const peaksPtr, const PoseModel poseModel, + template void connectBodyPartsGpu(Array& poseKeyPoints, float* posePtr, const float* const heatMapPtr, const float* const peaksPtr, const PoseModel poseModel, const cv::Size& heatMapSize, const int maxPeaks, const int interMinAboveThreshold, const float interThreshold, const int minSubsetCnt, const float minSubsetScore, const float scaleFactor); - template void connectBodyPartsGpu(Array& pose, double* posePtr, const double* const heatMapPtr, const double* const peaksPtr, const PoseModel poseModel, + template void connectBodyPartsGpu(Array& poseKeyPoints, double* posePtr, const double* const heatMapPtr, const double* const peaksPtr, const PoseModel poseModel, const cv::Size& heatMapSize, const int maxPeaks, const int interMinAboveThreshold, const double interThreshold, const int minSubsetCnt, const double minSubsetScore, const double scaleFactor); } diff --git a/src/openpose/pose/bodyPartConnectorCaffe.cpp b/src/openpose/pose/bodyPartConnectorCaffe.cpp index be3e158f..f420a1a3 100644 --- a/src/openpose/pose/bodyPartConnectorCaffe.cpp +++ b/src/openpose/pose/bodyPartConnectorCaffe.cpp @@ -131,7 +131,7 @@ namespace op } template - void BodyPartConnectorCaffe::Forward_cpu(const std::vector*>& bottom, Array& pose) + void BodyPartConnectorCaffe::Forward_cpu(const std::vector*>& bottom, Array& poseKeyPoints) { try { @@ -139,7 +139,7 @@ namespace op const auto* const heatMapsPtr = heatMapsBlob->cpu_data(); // ~8.5ms / 114 const auto* const peaksPtr = bottom.at(1)->cpu_data(); // ~0.02ms const auto maxPeaks = mTopSize[1]; - connectBodyPartsCpu(pose, heatMapsPtr, peaksPtr, mPoseModel, cv::Size{heatMapsBlob->shape(3), heatMapsBlob->shape(2)}, maxPeaks, + connectBodyPartsCpu(poseKeyPoints, heatMapsPtr, peaksPtr, mPoseModel, cv::Size{heatMapsBlob->shape(3), heatMapsBlob->shape(2)}, maxPeaks, mInterMinAboveThreshold, mInterThreshold, mMinSubsetCnt, mMinSubsetScore, mScaleNetToOutput); } catch (const std::exception& e) @@ -149,7 +149,7 @@ namespace op } template - void BodyPartConnectorCaffe::Forward_gpu(const std::vector*>& bottom, const std::vector*>& top, Array& pose) + void BodyPartConnectorCaffe::Forward_gpu(const std::vector*>& bottom, const std::vector*>& top, Array& poseKeyPoints) { try { @@ -157,7 +157,7 @@ namespace op const auto* const heatMapsPtr = heatMapsBlob->gpu_data(); const auto* const peaksPtr = bottom.at(1)->gpu_data(); const auto maxPeaks = mTopSize[1]; - connectBodyPartsGpu(pose, top.at(0)->mutable_gpu_data(), heatMapsPtr, peaksPtr, mPoseModel, cv::Size{heatMapsBlob->shape(3), heatMapsBlob->shape(2)}, maxPeaks, + connectBodyPartsGpu(poseKeyPoints, top.at(0)->mutable_gpu_data(), heatMapsPtr, peaksPtr, mPoseModel, cv::Size{heatMapsBlob->shape(3), heatMapsBlob->shape(2)}, maxPeaks, mInterMinAboveThreshold, mInterThreshold, mMinSubsetCnt, mMinSubsetScore, mScaleNetToOutput); } catch (const std::exception& e) diff --git a/src/openpose/pose/poseExtractor.cpp b/src/openpose/pose/poseExtractor.cpp index be34ec6b..6765abb6 100644 --- a/src/openpose/pose/poseExtractor.cpp +++ b/src/openpose/pose/poseExtractor.cpp @@ -163,12 +163,12 @@ namespace op } } - Array PoseExtractor::getPose() const + Array PoseExtractor::getPoseKeyPoints() const { try { checkThread(); - return mPose; + return mPoseKeyPoints; } catch (const std::exception& e) { diff --git a/src/openpose/pose/poseExtractorCaffe.cpp b/src/openpose/pose/poseExtractorCaffe.cpp index d702b750..a9bf9b46 100644 --- a/src/openpose/pose/poseExtractorCaffe.cpp +++ b/src/openpose/pose/poseExtractorCaffe.cpp @@ -107,8 +107,8 @@ namespace op spBodyPartConnectorCaffe->setMinSubsetScore((float)get(PoseProperty::ConnectMinSubsetScore)); // GPU version not implemented yet - spBodyPartConnectorCaffe->Forward_cpu({spHeatMapsBlob.get(), spPeaksBlob.get()}, mPose); - // spBodyPartConnectorCaffe->Forward_gpu({spHeatMapsBlob.get(), spPeaksBlob.get()}, {spPoseBlob.get()}, mPose); + spBodyPartConnectorCaffe->Forward_cpu({spHeatMapsBlob.get(), spPeaksBlob.get()}, mPoseKeyPoints); + // spBodyPartConnectorCaffe->Forward_gpu({spHeatMapsBlob.get(), spPeaksBlob.get()}, {spPoseBlob.get()}, mPoseKeyPoints); } catch (const std::exception& e) { diff --git a/src/openpose/pose/poseParameters.cpp b/src/openpose/pose/poseParameters.cpp new file mode 100644 index 00000000..f89bd8ef --- /dev/null +++ b/src/openpose/pose/poseParameters.cpp @@ -0,0 +1,37 @@ +#include "openpose/utilities/errorAndLog.hpp" +#include "openpose/pose/poseParameters.hpp" + +namespace op +{ + unsigned char poseBodyPartMapStringToKey(const PoseModel poseModel, const std::vector& strings) + { + try + { + const auto& poseBodyPartMapping = POSE_BODY_PART_MAPPING[(int)poseModel]; + for (auto& string : strings) + for (auto& pair : poseBodyPartMapping) + if (pair.second == string) + return pair.first; + error("String(s) could not be found.", __LINE__, __FUNCTION__, __FILE__); + return 0; + } + catch (const std::exception& e) + { + error(e.what(), __LINE__, __FUNCTION__, __FILE__); + return 0; + } + } + + unsigned char poseBodyPartMapStringToKey(const PoseModel poseModel, const std::string& string) + { + try + { + return poseBodyPartMapStringToKey(poseModel, std::vector{string}); + } + catch (const std::exception& e) + { + error(e.what(), __LINE__, __FUNCTION__, __FILE__); + return 0; + } + } +} diff --git a/src/openpose/pose/poseRenderGpu.cu b/src/openpose/pose/poseRenderGpu.cu index 468e58b4..8a62e0f5 100644 --- a/src/openpose/pose/poseRenderGpu.cu +++ b/src/openpose/pose/poseRenderGpu.cu @@ -3,6 +3,7 @@ #include "openpose/utilities/errorAndLog.hpp" #include "openpose/utilities/cuda.hpp" #include "openpose/utilities/cuda.hu" +#include "openpose/utilities/render.hu" #include "openpose/pose/poseRenderGpu.hpp" namespace op @@ -141,162 +142,29 @@ namespace op colorPtr.z *= rad; } - __global__ void renderPoseCoco(float* targetPtr, const int targetWidth, const int targetHeight, const float* const posePtr, - const int numberPeople, const float threshold, const bool googlyEyes, const float blendOriginalFrame, const float alphaColorToAdd) + __global__ void renderPoseCoco(float* targetPtr, const int targetWidth, const int targetHeight, const float* const posePtr, const int numberPeople, + const float threshold, const bool googlyEyes, const float blendOriginalFrame, const float alphaColorToAdd) { - //posePtr has length 3 * 18 * numberPeople const auto x = (blockIdx.x * blockDim.x) + threadIdx.x; const auto y = (blockIdx.y * blockDim.y) + threadIdx.y; const auto globalIdx = threadIdx.y * blockDim.x + threadIdx.x; - __shared__ float sharedPoses[POSE_COCO_NUMBER_PARTS*3*POSE_MAX_PEOPLE]; + // Shared parameters __shared__ float2 sharedMins[POSE_MAX_PEOPLE]; __shared__ float2 sharedMaxs[POSE_MAX_PEOPLE]; __shared__ float sharedScaleF[POSE_MAX_PEOPLE]; - if (globalIdx < numberPeople) - { - sharedMins[globalIdx].x = targetWidth; - sharedMins[globalIdx].y = targetHeight; - sharedMaxs[globalIdx].x = 0.f; - sharedMaxs[globalIdx].y = 0.f; - for (auto part = 0 ; part < POSE_COCO_NUMBER_PARTS ; part++) - { - const auto index = 3 * (globalIdx*POSE_COCO_NUMBER_PARTS + part); - const auto x = posePtr[index]; - const auto y = posePtr[index+1]; - const auto score = posePtr[index+2]; - sharedPoses[index] = x; - sharedPoses[index+1] = y; - sharedPoses[index+2] = score; - if (score > threshold) - { - if (x < sharedMins[globalIdx].x) - sharedMins[globalIdx].x = x; - if (x > sharedMaxs[globalIdx].x) - sharedMaxs[globalIdx].x = x; - if (y < sharedMins[globalIdx].y) - sharedMins[globalIdx].y = y; - if (y > sharedMaxs[globalIdx].y) - sharedMaxs[globalIdx].y = y; - } - } - const auto averageX = sharedMaxs[globalIdx].x-sharedMins[globalIdx].x; - const auto averageY = sharedMaxs[globalIdx].y-sharedMins[globalIdx].y; - sharedScaleF[globalIdx] = fastTruncate((averageX + averageY) / 400.f, 0.33f, 1.f); // (averageX + averageY) / 2.f / 400.f - const auto constantToAdd = 50.f; - sharedMaxs[globalIdx].x += constantToAdd; - sharedMaxs[globalIdx].y += constantToAdd; - sharedMins[globalIdx].x -= constantToAdd; - sharedMins[globalIdx].y -= constantToAdd; - } - __syncthreads(); + // Other parameters + const auto numberPartPairs = sizeof(COCO_PAIRS_GPU) / (2*sizeof(COCO_PAIRS_GPU[0])); + const auto numberColors = sizeof(COCO_RGB_COLORS) / (3*sizeof(COCO_RGB_COLORS[0])); + const auto radius = fastMin(targetWidth, targetHeight) / 100.f; + const auto stickwidth = fastMin(targetWidth, targetHeight) / 120.f; - const auto numberBodyParts = sizeof(COCO_PAIRS_GPU)/(2*sizeof(COCO_PAIRS_GPU[0])); - const auto numberColors = sizeof(COCO_RGB_COLORS)/(3*sizeof(COCO_RGB_COLORS[0])); - const auto radius = targetHeight / 100.f; - const auto stickwidth = targetHeight / 120.f; - - if (x < targetWidth && y < targetHeight) - { - const auto baseIndex = y * targetWidth + x; - auto& b = targetPtr[ baseIndex]; - auto& g = targetPtr[ targetWidth * targetHeight + baseIndex]; - auto& r = targetPtr[2 * targetWidth * targetHeight + baseIndex]; - if (!blendOriginalFrame) - { - b = 0.f; - g = 0.f; - r = 0.f; - } - - for (auto person = 0; person < numberPeople; person++) - { - if (x <= sharedMaxs[person].x && x >= sharedMins[person].x && y <= sharedMaxs[person].y && y >= sharedMins[person].y) - { - // Body part connections - for (auto bodyPart = 0; bodyPart < numberBodyParts; bodyPart++) - { - const auto bSqrt = sharedScaleF[person] * sharedScaleF[person] * stickwidth * stickwidth; - const auto partA = COCO_PAIRS_GPU[2*bodyPart]; - const auto partB = COCO_PAIRS_GPU[2*bodyPart+1]; - const auto indexA = person*POSE_COCO_NUMBER_PARTS*3 + partA*3; - const auto xA = sharedPoses[indexA]; - const auto yA = sharedPoses[indexA + 1]; - const auto valueA = sharedPoses[indexA + 2]; - const auto indexB = person*POSE_COCO_NUMBER_PARTS*3 + partB*3; - const auto xB = sharedPoses[indexB]; - const auto yB = sharedPoses[indexB + 1]; - const auto valueB = sharedPoses[indexB + 2]; - - if (valueA > threshold && valueB > threshold) - { - const auto xP = (xA + xB) / 2.f; - const auto yP = (yA + yB) / 2.f; - const auto aSqrt = (xA - xP) * (xA - xP) + (yA - yP) * (yA - yP); - - const auto angle = atan2f(yB - yA, xB - xA); - const auto sine = sinf(angle); - const auto cosine = cosf(angle); - const auto A = cosine * (x - xP) + sine * (y - yP); - const auto B = sine * (x - xP) - cosine * (y - yP); - - const auto judge = A * A / aSqrt + B * B / bSqrt; - const auto minV = 0.f; - const auto maxV = 1.f; - if (minV <= judge && judge <= maxV) - { - const auto index = (bodyPart%numberColors)*3; - addColorWeighted(r, g, b, &COCO_RGB_COLORS[index], alphaColorToAdd); - } - } - } - - // Body part circles - for (unsigned char i = 0; i < POSE_COCO_NUMBER_PARTS; i++) - { - const auto index = 3 * (person*POSE_COCO_NUMBER_PARTS + i); - const auto localX = sharedPoses[index]; - const auto localY = sharedPoses[index + 1]; - const auto value = sharedPoses[index + 2]; - - if (value > threshold) - { - const auto dist2 = (x - localX) * (x - localX) + (y - localY) * (y - localY); - - if (googlyEyes && (i==14 || i==15)) - { - const auto minr2 = sharedScaleF[person]*sharedScaleF[person]*(2.5*radius-2)*(2.5*radius-2); - const auto maxr2 = sharedScaleF[person]*sharedScaleF[person]*2.5*2.5*radius*radius; - if (dist2 <= maxr2) - { - float colorToAdd [3] = {0., 0., 0.}; - if (dist2 <= minr2) - for (auto& color : colorToAdd) - color = {255.f}; - if (dist2 <= minr2*0.6) - { - const auto dist3 = (x-4 - localX) * (x-4 - localX) + (y - localY+4) * (y - localY+4); - if (dist3 > 3.75f*3.75f) - for (auto& color : colorToAdd) - color = {0.f}; - } - const auto alphaColorToAdd = 0.9f; - addColorWeighted(r, g, b, colorToAdd, alphaColorToAdd); - } - } - else - { - const auto minr2 = 0.f; - const auto maxr2 = sharedScaleF[person]*sharedScaleF[person]*radius * radius; - if (minr2 <= dist2 && dist2 <= maxr2) - addColorWeighted(r, g, b, &COCO_RGB_COLORS[(i%numberColors)*3], alphaColorToAdd); - } - } - } - } - } - } + // Render key points + renderKeyPoints(targetPtr, sharedMaxs, sharedMins, sharedScaleF, + globalIdx, x, y, targetWidth, targetHeight, posePtr, COCO_PAIRS_GPU, numberPeople, + POSE_COCO_NUMBER_PARTS, numberPartPairs, COCO_RGB_COLORS, numberColors, + radius, stickwidth, threshold, alphaColorToAdd); } __global__ void renderPoseMpi29Parts(float* targetPtr, const int targetWidth, const int targetHeight, const float* const posePtr, @@ -305,25 +173,13 @@ namespace op //posePtr has length 3 * 15 * numberPeople const auto x = (blockIdx.x * blockDim.x) + threadIdx.x; const auto y = (blockIdx.y * blockDim.y) + threadIdx.y; - const auto globalIdx = threadIdx.y * blockDim.x + threadIdx.x; - - __shared__ float sharedPoses[POSE_MPI_NUMBER_PARTS*3*POSE_MAX_PEOPLE]; - if (globalIdx < numberPeople * POSE_MPI_NUMBER_PARTS) - { - const auto index = 3*globalIdx; - sharedPoses[index] = posePtr[index]; - sharedPoses[index+1] = posePtr[index+1]; - sharedPoses[index+2] = posePtr[index+2]; - } - - __syncthreads(); - - const auto numberBodyParts = sizeof(MPI_PAIRS_GPU)/(2*sizeof(MPI_PAIRS_GPU[0])); - const auto radius = 3.f*targetHeight / 200.0f; - const auto stickwidth = targetHeight / 60.0f; if (x < targetWidth && y < targetHeight) { + const auto numberBodyParts = sizeof(MPI_PAIRS_GPU)/(2*sizeof(MPI_PAIRS_GPU[0])); + const auto radius = 3.f*targetHeight / 200.0f; + const auto stickwidth = targetHeight / 60.0f; + const auto blueIndex = y * targetWidth + x; auto& b = targetPtr[ blueIndex]; auto& g = targetPtr[ targetWidth * targetHeight + blueIndex]; @@ -343,12 +199,12 @@ namespace op auto bSqrt = stickwidth * stickwidth; //fixed const auto partA = MPI_PAIRS_GPU[2*bodyPart]; const auto partB = MPI_PAIRS_GPU[2*bodyPart+1]; - const auto xA = sharedPoses[person*POSE_MPI_NUMBER_PARTS*3 + partA*3]; - const auto yA = sharedPoses[person*POSE_MPI_NUMBER_PARTS*3 + partA*3 + 1]; - const auto valueA = sharedPoses[person*POSE_MPI_NUMBER_PARTS*3 + partA*3 + 2]; - const auto xB = sharedPoses[person*POSE_MPI_NUMBER_PARTS*3 + partB*3]; - const auto yB = sharedPoses[person*POSE_MPI_NUMBER_PARTS*3 + partB*3 + 1]; - const auto valueB = sharedPoses[person*POSE_MPI_NUMBER_PARTS*3 + partB*3 + 2]; + const auto xA = posePtr[person*POSE_MPI_NUMBER_PARTS*3 + partA*3]; + const auto yA = posePtr[person*POSE_MPI_NUMBER_PARTS*3 + partA*3 + 1]; + const auto valueA = posePtr[person*POSE_MPI_NUMBER_PARTS*3 + partA*3 + 2]; + const auto xB = posePtr[person*POSE_MPI_NUMBER_PARTS*3 + partB*3]; + const auto yB = posePtr[person*POSE_MPI_NUMBER_PARTS*3 + partB*3 + 1]; + const auto valueB = posePtr[person*POSE_MPI_NUMBER_PARTS*3 + partB*3 + 2]; if (valueA > threshold && valueB > threshold) { const auto xP = (xA + xB) / 2.f; @@ -379,9 +235,9 @@ namespace op for (unsigned char i = 0; i < POSE_MPI_NUMBER_PARTS; i++) //for every point { const auto index = 3 * (person*POSE_MPI_NUMBER_PARTS + i); - const auto localX = sharedPoses[index]; - const auto localY = sharedPoses[index + 1]; - const auto value = sharedPoses[index + 2]; + const auto localX = posePtr[index]; + const auto localY = posePtr[index + 1]; + const auto value = posePtr[index + 2]; if (value > threshold) if ((x - localX) * (x - localX) + (y - localY) * (y - localY) <= radius * radius) @@ -548,7 +404,7 @@ namespace op } } - inline void gpuRenderPartAffinityAux(float* framePtr, const PoseModel poseModel, const cv::Size& frameSize, const float* const heatMapPtr, + inline void renderKeyPointsPartAffinityAux(float* framePtr, const PoseModel poseModel, const cv::Size& frameSize, const float* const heatMapPtr, const cv::Size& heatMapSize, const float scaleToKeepRatio, const int part, const int partsToRender, const float alphaBlending) { try @@ -656,7 +512,7 @@ namespace op { try { - gpuRenderPartAffinityAux(framePtr, poseModel, frameSize, heatMapPtr, heatMapSize, scaleToKeepRatio, part, 1, alphaBlending); + renderKeyPointsPartAffinityAux(framePtr, poseModel, frameSize, heatMapPtr, heatMapSize, scaleToKeepRatio, part, 1, alphaBlending); } catch (const std::exception& e) { @@ -670,7 +526,7 @@ namespace op try { const auto numberBodyPartPairs = POSE_BODY_PART_PAIRS[(int)poseModel].size()/2; - gpuRenderPartAffinityAux(framePtr, poseModel, frameSize, heatMapPtr, heatMapSize, scaleToKeepRatio, POSE_NUMBER_BODY_PARTS[(int)poseModel]+1, + renderKeyPointsPartAffinityAux(framePtr, poseModel, frameSize, heatMapPtr, heatMapSize, scaleToKeepRatio, POSE_NUMBER_BODY_PARTS[(int)poseModel]+1, numberBodyPartPairs, alphaBlending); } catch (const std::exception& e) diff --git a/src/openpose/pose/poseRenderer.cpp b/src/openpose/pose/poseRenderer.cpp index f39d5c8a..418102ac 100644 --- a/src/openpose/pose/poseRenderer.cpp +++ b/src/openpose/pose/poseRenderer.cpp @@ -211,7 +211,7 @@ namespace op } } - std::pair PoseRenderer::renderPose(Array& outputData, const Array& pose, const double scaleNetToOutput) + std::pair PoseRenderer::renderPose(Array& outputData, const Array& poseKeyPoints, const double scaleNetToOutput) { try { @@ -221,7 +221,7 @@ namespace op const auto elementRendered = mElementToRender.load(); // I prefer std::round(T&) over intRound(T) for std::atomic std::string elementRenderedName; - const auto numberPeople = pose.getSize(0); + const auto numberPeople = poseKeyPoints.getSize(0); // GPU rendering if (numberPeople > 0 || elementRendered != 0 || !mBlendOriginalFrame) @@ -230,11 +230,11 @@ namespace op cudaCheck(__LINE__, __FUNCTION__, __FILE__); const auto numberBodyParts = POSE_NUMBER_BODY_PARTS[(int)mPoseModel]; const auto numberBodyPartsPlusBkg = numberBodyParts+1; - // Draw pose + // Draw poseKeyPoints if (elementRendered == 0) { - if (!pose.empty()) - cudaMemcpy(pGpuPose, pose.getConstPtr(), numberPeople * numberBodyParts * 3 * sizeof(float), cudaMemcpyHostToDevice); + if (!poseKeyPoints.empty()) + cudaMemcpy(pGpuPose, poseKeyPoints.getConstPtr(), numberPeople * numberBodyParts * 3 * sizeof(float), cudaMemcpyHostToDevice); renderPoseGpu(*spGpuMemoryPtr, mPoseModel, numberPeople, mOutputSize, pGpuPose, mShowGooglyEyes, mBlendOriginalFrame, mAlphaPose); } else diff --git a/src/openpose/wrapper/wrapperStructFace.cpp b/src/openpose/wrapper/wrapperStructFace.cpp new file mode 100644 index 00000000..a5b5e7e8 --- /dev/null +++ b/src/openpose/wrapper/wrapperStructFace.cpp @@ -0,0 +1,12 @@ +#include "openpose/wrapper/wrapperStructFace.hpp" + +namespace op +{ + namespace experimental + { + WrapperStructFace::WrapperStructFace(const bool extractAndRenderFace_) : + extractAndRenderFace{extractAndRenderFace_} + { + } + } +} diff --git a/src/openpose/wrapper/wrapperStructHands.cpp b/src/openpose/wrapper/wrapperStructHand.cpp similarity index 50% rename from src/openpose/wrapper/wrapperStructHands.cpp rename to src/openpose/wrapper/wrapperStructHand.cpp index 36f2d779..e2f2dc46 100644 --- a/src/openpose/wrapper/wrapperStructHands.cpp +++ b/src/openpose/wrapper/wrapperStructHand.cpp @@ -1,10 +1,10 @@ -#include "openpose/wrapper/wrapperStructHands.hpp" +#include "openpose/wrapper/wrapperStructHand.hpp" namespace op { namespace experimental { - WrapperStructHands::WrapperStructHands(const bool extractAndRenderHands_) : + WrapperStructHand::WrapperStructHand(const bool extractAndRenderHands_) : extractAndRenderHands{extractAndRenderHands_} { }