mirror of
https://github.com/gosticks/openpose.git
synced 2026-08-12 04:10:28 +00:00
394 lines
17 KiB
C++
394 lines
17 KiB
C++
#ifndef OPENPOSE_WRAPPER_WRAPPER_HAND_FROM_JSON_TEST_HPP
|
|
#define OPENPOSE_WRAPPER_WRAPPER_HAND_FROM_JSON_TEST_HPP
|
|
|
|
// Third-party dependencies
|
|
#include <opencv2/opencv.hpp>
|
|
// OpenPose dependencies
|
|
#include <openpose/headers.hpp>
|
|
|
|
namespace op
|
|
{
|
|
template<typename TDatum,
|
|
typename TDatums = std::vector<std::shared_ptr<TDatum>>,
|
|
typename TWorker = std::shared_ptr<Worker<std::shared_ptr<TDatums>>>,
|
|
typename TQueue = Queue<std::shared_ptr<TDatums>>>
|
|
class WrapperHandFromJsonTest
|
|
{
|
|
public:
|
|
/**
|
|
* Constructor.
|
|
*/
|
|
explicit WrapperHandFromJsonTest();
|
|
|
|
/**
|
|
* Destructor.
|
|
* It automatically frees resources.
|
|
*/
|
|
~WrapperHandFromJsonTest();
|
|
|
|
void configure(const WrapperStructPose& wrapperStructPose,
|
|
const WrapperStructHand& wrapperStructHand,
|
|
const std::shared_ptr<Producer>& producerSharedPtr,
|
|
const std::string& handGroundTruth,
|
|
const std::string& writeJson,
|
|
const DisplayMode displayMode = DisplayMode::NoDisplay);
|
|
|
|
/**
|
|
* Function to start multi-threading.
|
|
* Similar to start(), but exec() blocks the thread that calls the function (it saves 1 thread). Use exec() instead of
|
|
* start() if the calling thread will otherwise be waiting for the WrapperHandFromJsonTest to end.
|
|
*/
|
|
void exec();
|
|
|
|
private:
|
|
ThreadManager<std::shared_ptr<TDatums>> mThreadManager;
|
|
// Workers
|
|
TWorker wDatumProducer;
|
|
TWorker spWIdGenerator;
|
|
TWorker spWScaleAndSizeExtractor;
|
|
TWorker spWCvMatToOpInput;
|
|
TWorker spWCvMatToOpOutput;
|
|
std::vector<std::vector<TWorker>> spWPoses;
|
|
std::vector<TWorker> mPostProcessingWs;
|
|
std::vector<TWorker> mOutputWs;
|
|
TWorker spWGui;
|
|
|
|
/**
|
|
* Frees TWorker variables (private internal function).
|
|
* For most cases, this class is non-necessary, since std::shared_ptr are automatically cleaned on destruction of each class.
|
|
* However, it might be useful if the same WrapperHandFromJsonTest is gonna be started twice (not recommended on most cases).
|
|
*/
|
|
void reset();
|
|
|
|
/**
|
|
* Set ThreadManager from TWorkers (private internal function).
|
|
* After any configure() has been called, the TWorkers are initialized. This function resets the ThreadManager and adds them.
|
|
* Common code for start() and exec().
|
|
*/
|
|
void configureThreadManager();
|
|
|
|
/**
|
|
* TWorker concatenator (private internal function).
|
|
* Auxiliary function that concatenate std::vectors of TWorker. Since TWorker is some kind of smart pointer (usually
|
|
* std::shared_ptr), its copy still shares the same internal data. It will not work for TWorker classes that do not share
|
|
* the data when moved.
|
|
* @param workersA First std::shared_ptr<TDatums> element to be concatenated.
|
|
* @param workersB Second std::shared_ptr<TDatums> element to be concatenated.
|
|
* @return Concatenated std::vector<TWorker> of both workersA and workersB.
|
|
*/
|
|
std::vector<TWorker> mergeWorkers(const std::vector<TWorker>& workersA, const std::vector<TWorker>& workersB);
|
|
|
|
DELETE_COPY(WrapperHandFromJsonTest);
|
|
};
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Implementation
|
|
#include <openpose/core/headers.hpp>
|
|
#include <openpose/face/headers.hpp>
|
|
#include <openpose/filestream/headers.hpp>
|
|
#include <openpose/gui/headers.hpp>
|
|
#include <openpose/gpu/gpu.hpp>
|
|
#include <openpose/hand/headers.hpp>
|
|
#include <openpose/pose/headers.hpp>
|
|
#include <openpose/producer/headers.hpp>
|
|
#include <openpose/utilities/errorAndLog.hpp>
|
|
#include <openpose/utilities/fileSystem.hpp>
|
|
namespace op
|
|
{
|
|
template<typename TDatum, typename TDatums, typename TWorker, typename TQueue>
|
|
WrapperHandFromJsonTest<TDatum, TDatums, TWorker, TQueue>::WrapperHandFromJsonTest()
|
|
{
|
|
}
|
|
|
|
template<typename TDatum, typename TDatums, typename TWorker, typename TQueue>
|
|
WrapperHandFromJsonTest<TDatum, TDatums, TWorker, TQueue>::~WrapperHandFromJsonTest()
|
|
{
|
|
try
|
|
{
|
|
mThreadManager.stop();
|
|
reset();
|
|
}
|
|
catch (const std::exception& e)
|
|
{
|
|
errorDestructor(e.what(), __LINE__, __FUNCTION__, __FILE__);
|
|
}
|
|
}
|
|
|
|
template<typename TDatum, typename TDatums, typename TWorker, typename TQueue>
|
|
void WrapperHandFromJsonTest<TDatum, TDatums, TWorker, TQueue>::configure(
|
|
const WrapperStructPose& wrapperStructPose,
|
|
const WrapperStructHand& wrapperStructHand,
|
|
const std::shared_ptr<Producer>& producerSharedPtr,
|
|
const std::string& handGroundTruth,
|
|
const std::string& writeJson,
|
|
const DisplayMode displayMode)
|
|
{
|
|
try
|
|
{
|
|
opLog("", Priority::Low, __LINE__, __FUNCTION__, __FILE__);
|
|
|
|
// Shortcut
|
|
typedef std::shared_ptr<TDatums> TDatumsPtr;
|
|
|
|
// Check no wrong/contradictory flags enabled
|
|
if (wrapperStructPose.scaleGap <= 0.f && wrapperStructPose.scalesNumber > 1)
|
|
error("The scale gap must be greater than 0 (it has no effect if the number of scales is 1).",
|
|
__LINE__, __FUNCTION__, __FILE__);
|
|
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 = !writeJson.empty();
|
|
const auto displayGui = (displayMode != DisplayMode::NoDisplay);
|
|
if (!displayGui && !savingSomething)
|
|
{
|
|
const auto message = "No output is selected (`--display 0`) and no results are generated (no `write_X` flags enabled). Thus,"
|
|
" no output would be generated." + additionalMessage;
|
|
error(message, __LINE__, __FUNCTION__, __FILE__);
|
|
}
|
|
|
|
// Get number GPUs
|
|
auto gpuNumber = wrapperStructPose.gpuNumber;
|
|
auto gpuNumberStart = wrapperStructPose.gpuNumberStart;
|
|
// If number GPU < 0 --> set it to all the available GPUs
|
|
if (gpuNumber < 0)
|
|
{
|
|
// Get total number GPUs
|
|
gpuNumber = getGpuNumber();
|
|
// Reset initial GPU to 0 (we want them all)
|
|
gpuNumberStart = 0;
|
|
// Logging message
|
|
opLog("Auto-detecting GPUs... Detected " + std::to_string(gpuNumber) + " GPU(s), using them all.", Priority::High);
|
|
}
|
|
|
|
// Proper format
|
|
const auto writeJsonCleaned = formatAsDirectory(writeJson);
|
|
|
|
// Common parameters
|
|
const auto finalOutputSize = wrapperStructPose.outputSize;
|
|
const Point<int> producerSize{
|
|
(int)producerSharedPtr->get(getCvCapPropFrameWidth()),
|
|
(int)producerSharedPtr->get(getCvCapPropFrameHeight())};
|
|
if (finalOutputSize.x == -1 || finalOutputSize.y == -1)
|
|
{
|
|
const auto message = "Output resolution cannot be (-1 x -1) unless producerSharedPtr is also set.";
|
|
error(message, __LINE__, __FUNCTION__, __FILE__);
|
|
}
|
|
|
|
// Producer
|
|
const auto datumProducer = std::make_shared<DatumProducer<TDatum>>(producerSharedPtr);
|
|
wDatumProducer = std::make_shared<WDatumProducer<TDatum>>(datumProducer);
|
|
|
|
// Get input scales and sizes
|
|
const auto scaleAndSizeExtractor = std::make_shared<ScaleAndSizeExtractor>(
|
|
wrapperStructPose.netInputSize, finalOutputSize, wrapperStructPose.scalesNumber,
|
|
wrapperStructPose.scaleGap
|
|
);
|
|
spWScaleAndSizeExtractor = std::make_shared<WScaleAndSizeExtractor<TDatumsPtr>>(scaleAndSizeExtractor);
|
|
|
|
// Input cvMat to OpenPose format
|
|
const auto cvMatToOpInput = std::make_shared<CvMatToOpInput>(wrapperStructPose.poseModel);
|
|
spWCvMatToOpInput = std::make_shared<WCvMatToOpInput<TDatumsPtr>>(cvMatToOpInput);
|
|
if (displayGui)
|
|
{
|
|
const auto cvMatToOpOutput = std::make_shared<CvMatToOpOutput>();
|
|
spWCvMatToOpOutput = std::make_shared<WCvMatToOpOutput<TDatumsPtr>>(cvMatToOpOutput);
|
|
}
|
|
|
|
// Hand extractor(s)
|
|
if (wrapperStructHand.enable)
|
|
{
|
|
spWPoses.resize(gpuNumber);
|
|
const auto handDetector = std::make_shared<HandDetectorFromTxt>(handGroundTruth);
|
|
for (auto gpuId = 0u; gpuId < spWPoses.size(); gpuId++)
|
|
{
|
|
// Hand detector
|
|
// If tracking
|
|
if (wrapperStructHand.detector == Detector::BodyWithTracking)
|
|
error("Tracking not valid for hand detector from JSON files.", __LINE__, __FUNCTION__, __FILE__);
|
|
// If detection
|
|
else
|
|
spWPoses.at(gpuId) = {std::make_shared<WHandDetectorFromTxt<TDatumsPtr>>(handDetector)};
|
|
// Hand keypoint extractor
|
|
const auto netOutputSize = wrapperStructHand.netInputSize;
|
|
const auto handExtractor = std::make_shared<HandExtractorCaffe>(
|
|
wrapperStructHand.netInputSize, netOutputSize, wrapperStructPose.modelFolder.getStdString(),
|
|
gpuId + gpuNumberStart, wrapperStructHand.scalesNumber, wrapperStructHand.scaleRange
|
|
);
|
|
spWPoses.at(gpuId).emplace_back(std::make_shared<WHandExtractorNet<TDatumsPtr>>(handExtractor));
|
|
}
|
|
}
|
|
|
|
// Hand renderer(s)
|
|
std::vector<TWorker> cpuRenderers;
|
|
if (displayGui)
|
|
{
|
|
// Construct hand renderer
|
|
const auto handRenderer = std::make_shared<HandCpuRenderer>(wrapperStructHand.renderThreshold,
|
|
wrapperStructHand.alphaKeypoint,
|
|
wrapperStructHand.alphaHeatMap);
|
|
// Add worker
|
|
cpuRenderers.emplace_back(std::make_shared<WHandRenderer<TDatumsPtr>>(handRenderer));
|
|
}
|
|
|
|
// Itermediate workers (e.g., OpenPose format to cv::Mat, json & frames recorder, ...)
|
|
mPostProcessingWs.clear();
|
|
// Frame buffer and ordering
|
|
if (spWPoses.size() > 1)
|
|
mPostProcessingWs.emplace_back(std::make_shared<WQueueOrderer<TDatumsPtr>>());
|
|
// Frames processor (OpenPose format -> cv::Mat format)
|
|
if (displayGui)
|
|
{
|
|
mPostProcessingWs = mergeWorkers(mPostProcessingWs, cpuRenderers);
|
|
const auto opOutputToCvMat = std::make_shared<OpOutputToCvMat>();
|
|
mPostProcessingWs.emplace_back(std::make_shared<WOpOutputToCvMat<TDatumsPtr>>(opOutputToCvMat));
|
|
}
|
|
// Re-scale pose if desired
|
|
if (wrapperStructPose.keypointScaleMode != ScaleMode::InputResolution)
|
|
error("Only wrapperStructPose.keypointScaleMode == ScaleMode::InputResolution.",
|
|
__LINE__, __FUNCTION__, __FILE__);
|
|
|
|
mOutputWs.clear();
|
|
// Write people pose data on disk (json format)
|
|
if (!writeJsonCleaned.empty())
|
|
{
|
|
const auto jsonSaver = std::make_shared<PeopleJsonSaver>(writeJsonCleaned);
|
|
mOutputWs.emplace_back(std::make_shared<WPeopleJsonSaver<TDatumsPtr>>(jsonSaver));
|
|
}
|
|
// Minimal graphical user interface (GUI)
|
|
spWGui = nullptr;
|
|
if (displayGui)
|
|
{
|
|
const auto guiInfoAdder = std::make_shared<GuiInfoAdder>(gpuNumber, displayGui);
|
|
mOutputWs.emplace_back(std::make_shared<WGuiInfoAdder<TDatumsPtr>>(guiInfoAdder));
|
|
const auto gui = std::make_shared<Gui>(
|
|
finalOutputSize, false, mThreadManager.getIsRunningSharedPtr()
|
|
);
|
|
spWGui = {std::make_shared<WGui<TDatumsPtr>>(gui)};
|
|
}
|
|
opLog("", Priority::Low, __LINE__, __FUNCTION__, __FILE__);
|
|
}
|
|
catch (const std::exception& e)
|
|
{
|
|
error(e.what(), __LINE__, __FUNCTION__, __FILE__);
|
|
}
|
|
}
|
|
|
|
template<typename TDatum, typename TDatums, typename TWorker, typename TQueue>
|
|
void WrapperHandFromJsonTest<TDatum, TDatums, TWorker, TQueue>::exec()
|
|
{
|
|
try
|
|
{
|
|
configureThreadManager();
|
|
mThreadManager.exec();
|
|
}
|
|
catch (const std::exception& e)
|
|
{
|
|
error(e.what(), __LINE__, __FUNCTION__, __FILE__);
|
|
}
|
|
}
|
|
|
|
template<typename TDatum, typename TDatums, typename TWorker, typename TQueue>
|
|
void WrapperHandFromJsonTest<TDatum, TDatums, TWorker, TQueue>::reset()
|
|
{
|
|
try
|
|
{
|
|
mThreadManager.reset();
|
|
// Reset
|
|
wDatumProducer = nullptr;
|
|
spWScaleAndSizeExtractor = nullptr;
|
|
spWCvMatToOpInput = nullptr;
|
|
spWCvMatToOpOutput = nullptr;
|
|
spWPoses.clear();
|
|
mPostProcessingWs.clear();
|
|
mOutputWs.clear();
|
|
spWGui = nullptr;
|
|
}
|
|
catch (const std::exception& e)
|
|
{
|
|
error(e.what(), __LINE__, __FUNCTION__, __FILE__);
|
|
}
|
|
}
|
|
|
|
template<typename TDatum, typename TDatums, typename TWorker, typename TQueue>
|
|
void WrapperHandFromJsonTest<TDatum, TDatums, TWorker, TQueue>::configureThreadManager()
|
|
{
|
|
try
|
|
{
|
|
// Sanity checks
|
|
if (spWCvMatToOpInput == nullptr)
|
|
error("Configure the WrapperHandFromJsonTest class before calling `start()`.",
|
|
__LINE__, __FUNCTION__, __FILE__);
|
|
if (wDatumProducer == nullptr)
|
|
{
|
|
const auto message = "You need to use the OpenPose default producer.";
|
|
error(message, __LINE__, __FUNCTION__, __FILE__);
|
|
}
|
|
if (mOutputWs.empty() && spWGui == nullptr)
|
|
{
|
|
error("No output selected.", __LINE__, __FUNCTION__, __FILE__);
|
|
}
|
|
|
|
// Thread Manager:
|
|
// Clean previous thread manager (avoid configure to crash the program if used more than once)
|
|
mThreadManager.reset();
|
|
auto threadId = 0ull;
|
|
auto queueIn = 0ull;
|
|
auto queueOut = 1ull;
|
|
// If custom user Worker in same thread or producer on same thread
|
|
spWIdGenerator = std::make_shared<WIdGenerator<std::shared_ptr<TDatums>>>();
|
|
// OpenPose producer
|
|
// Thread 0 or 1, queues 0 -> 1
|
|
if (spWCvMatToOpOutput == nullptr)
|
|
mThreadManager.add(threadId++, {wDatumProducer, spWIdGenerator, spWScaleAndSizeExtractor,
|
|
spWCvMatToOpInput}, queueIn++, queueOut++);
|
|
else
|
|
mThreadManager.add(threadId++, {wDatumProducer, spWIdGenerator, spWScaleAndSizeExtractor,
|
|
spWCvMatToOpInput, spWCvMatToOpOutput}, queueIn++, queueOut++);
|
|
// Pose estimation & rendering
|
|
// Thread 1 or 2...X, queues 1 -> 2, X = 2 + #GPUs
|
|
if (!spWPoses.empty())
|
|
{
|
|
for (auto& wPose : spWPoses)
|
|
mThreadManager.add(threadId++, wPose, queueIn, queueOut);
|
|
queueIn++;
|
|
queueOut++;
|
|
}
|
|
// If custom user Worker in same thread or producer on same thread
|
|
// Post processing workers + User post processing workers + Output workers
|
|
// Thread 2 or 3, queues 2 -> 3
|
|
mThreadManager.add(threadId++, mergeWorkers(mPostProcessingWs, mOutputWs), queueIn++, queueOut++);
|
|
// OpenPose GUI
|
|
// Thread Y+1, queues Q+1 -> Q+2
|
|
if (spWGui != nullptr)
|
|
mThreadManager.add(threadId++, spWGui, queueIn++, queueOut++);
|
|
opLog("", Priority::Low, __LINE__, __FUNCTION__, __FILE__);
|
|
}
|
|
catch (const std::exception& e)
|
|
{
|
|
error(e.what(), __LINE__, __FUNCTION__, __FILE__);
|
|
}
|
|
}
|
|
|
|
template<typename TDatum, typename TDatums, typename TWorker, typename TQueue>
|
|
std::vector<TWorker> WrapperHandFromJsonTest<TDatum, TDatums, TWorker, TQueue>::mergeWorkers(const std::vector<TWorker>& workersA, const std::vector<TWorker>& workersB)
|
|
{
|
|
try
|
|
{
|
|
auto workersToReturn(workersA);
|
|
for (auto& worker : workersB)
|
|
workersToReturn.emplace_back(worker);
|
|
return workersToReturn;
|
|
}
|
|
catch (const std::exception& e)
|
|
{
|
|
error(e.what(), __LINE__, __FUNCTION__, __FILE__);
|
|
return std::vector<TWorker>{};
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif // OPENPOSE_WRAPPER_WRAPPER_HAND_FROM_JSON_TEST_HPP
|